一、指針:指向一個內存地址的變量或參數。
二、定義指針的方式如下:
P: Pointer; //定義了可以指向任何類型的指針,Pointer 為無類型指針;
Q, R: ^TType; //定義了指向 TType 類型的兩個指針,TType 可是是各種
//基本類型或自己定義的各種類型,也可以如下定義:
// type PType = ^TType;
// var Q, R: PType;
三、指針的使用:
P: Pointer;
Q, R: ^TType;
A: TType;
Q:= R; //相同類型可以賦值
P:= R; //可以把任何類型指針賦給無類型指針
R:= P; //可以把無類型指針賦給有類型的指針
Q:= @A; //加 @ 后,得到指向 A 的無類型指針,然后賦給 Q
New(Q); //分配一個 TType 類型的空間,並把地址賦給 Q
Dispose(Q); //釋放 Q 所指向的空間
四、指針的強制轉換
type TType = record ... end;
PType = ^record ... end;
var P: ^Integer;
A: TType;
P:= @A; //加 @ 后,得到指向 A 的無類型指針,可以賦給 P
PType(P).xxx //把 P 轉換為 PType 類型,並引用記錄中的值
with PType(P)^ do begin ... end; // 注意 ^ 的使用,在上一行中,
//不需要加 ^ (當然加也不錯),編譯器可以自動識別,
//而在這一行中,如不加 ^ ,會引起混淆。
例子(把 string 的一個漢字當作 WideChar 並賦給一個整型變量):
A: Integer; S: string;
A:= Word(PWideChar(@S[3])^);
// @S[3] 得到 S[3] 的地址,然后把它當作指向 WideChar 的指針,
// 用 ^ 取出一個漢字,再用 Word 強制轉換,最后賦給一個整型變量。
// 用 Word 轉換的原因是 Word 的字節數和 WideChar 的字節數一樣。
// 在 Delphi 中,只要字節數一樣的類型,都可以用強制轉換,如字節
// 不一樣,就要用函數,這也是很合情理的。
// 一定要這么寫是讓程序員知道自己在干什么,不至於出錯,真正的實現
// 是很簡單的。
五、函數類型
函數類型也是指針,定義函數類型如下:
type TFunc = function (X: Integer; S: string): Boolean;
TProc = procedure;
var F, G: function(A: Integer): string;
function Test(B: Integer): string;
使用:
F:= nil; //函數類型是指針
F:= Test; //當函數類型在賦值號左面,右面是一個函數時當作是對函數
//類型賦值,其它情況(如右面不是一個函數或函數類型在左
//面或不是賦值號等),則認為是調用指向的函數,如下面一行
S:= F(3); // 直接調用函數 Test(3);
F:= @Test; // 取 Test 函數的地址並賦給 F,所以這樣也正確
@F:= @Test; //對函數類型取地址,得到的是所指向函數的地址,所以這樣也正確
@@F ... // 若想取函數類型的地址用兩個 @,即 @@
@F = @Test; // 判斷 F 是否指向 Test,通過比較兩個無類型指針得到
Assigned(F) // 因為 F = nil 中左側是調用 F,為了判斷 F 是否不為空,
// 用 Assigned
六、方法指針
這里指的是類內的函數類型,是一對指針,一個指向函數地址,另一個存放
實例的引用。
定義如下:
TMethod = procedure (...) of object;
使用:
type TTest = class procedure T(...); end;
var M: TMethod;
M:= TTest.T;
因為這里是一對指針,與其他指針的轉換就有些問題,這里就不說了。
七、集合、數組、動態數組、記錄等
這些是特定的類型,Delphi 沒有說他們是指針類型,這和 C 是有區別的
(隨便什么都可以當指針用)。這一點請特別注意。
要想使他們和指針取得聯系,一般要使用地址符 @,其實這也是很方便的,
如上面的漢字例子。還有前面討論的 BlockRead 等例子都是這樣。
S: array of record ... end;
SetLength(S, N);
BlockRead(F, S[0], N);
~~~ 這里沒有 @ 符的原因是它是 var 參數,不加 @ 就會把
S[0] 的地址傳進去。