在學(xué)習(xí)C的時(shí)候最讓人頭通的可能就是C中的指針了,但也正是這指針才是C的精華之所在
大家都認(rèn)為,C語(yǔ)言之所以強(qiáng)大,以及其自由性,很大部分體現(xiàn)在其靈活的指針運(yùn)用上。因此,說(shuō)指針是C語(yǔ)言的靈魂,一點(diǎn)都不為過(guò)。同時(shí),這種說(shuō)法也讓很多人 產(chǎn)生誤解,似乎只有C語(yǔ)言的指針才能算指針。Basic不支持指針,在此不論。其實(shí),Pascal語(yǔ)言本身也是支持指針的。從最初的Pascal發(fā)展至今的Object Pascal,可以說(shuō)在指針運(yùn)用上,絲毫不會(huì)遜色于C語(yǔ)言的指針。以下內(nèi)容分為八個(gè)部分,分別是:
一、類型指針的定義
二、無(wú)類型指針的定義
三、指針的解除引用
四、取地址(指針賦值)
五、指針運(yùn)算
六、動(dòng)態(tài)內(nèi)存分配
七、字符數(shù)組的運(yùn)算
八、函數(shù)指針
一、類型指針的定義。對(duì)于指向特定類型的指針,在C中是這樣定義的:
int *ptr;
char *ptr;
與之等價(jià)的Object Pascal是如何定義的呢?
var
ptr : ^Integer;
ptr : ^char;
其實(shí)也就是符號(hào)的差別而已。
二、無(wú)類型指針的定義。C中有void *類型,也就是可以指向任何類型數(shù)據(jù)的指針。Object Pascal為其定
義了一個(gè)專門的類型:Pointer。于是,
ptr : Pointer;
就與C中的
void *ptr;
等價(jià)了。
三、指針的解除引用。要解除指針引用(即取出指針?biāo)竻^(qū)域的值),C 的語(yǔ)法是 (*ptr),Object
Pascal則是 ptr^。
四、取地址(指針賦值)。取某對(duì)象的地址并將其賦值給指針變量,C 的語(yǔ)法是
ptr = &Object;
Object Pascal 則是
ptr := @Object;
也只是符號(hào)的差別而已。
五、指針運(yùn)算。在C中,可以對(duì)指針進(jìn)行移動(dòng)的運(yùn)算,如:
char a[20];
char *ptr=a;
ptr++;
ptr+=2;
當(dāng)執(zhí)行ptr++;時(shí),編譯器會(huì)產(chǎn)生讓ptr前進(jìn)sizeof(char)步長(zhǎng)的代碼,之后,ptr將指向a[1]。ptr+=2;這
句使得ptr前進(jìn)兩 個(gè)sizeof(char)大小的步長(zhǎng)。同樣,我們來(lái)看一下Object Pascal中如何實(shí)現(xiàn):
var
a : array [1..20] of Char;
ptr : PChar; //PChar 可以看作 ^Char
begin
ptr := @a;
Inc(ptr); // 這句等價(jià)于 C 的 ptr++;
Inc(ptr, 2); //這句等價(jià)于 C 的 ptr+=2;
end;
六、動(dòng)態(tài)內(nèi)存分配。C中,使用malloc()庫(kù)函數(shù)分配內(nèi)存,free()函數(shù)釋放內(nèi)存。如這樣的代碼:
int *ptr, *ptr2;
int i;
ptr = (int*) malloc(sizeof(int) * 20);
ptr2 = ptr;
for (i=0; i<20; i++){
*ptr = i;
ptr++;
}
free(ptr2);
Object Pascal中,動(dòng)態(tài)分配內(nèi)存的函數(shù)是GetMem(),與之對(duì)應(yīng)的釋放函數(shù)為FreeMem()(傳統(tǒng)Pascal中獲
取內(nèi)存的函數(shù)是New()和 Dispose(),但New()只能獲得對(duì)象的單個(gè)實(shí)體的內(nèi)存大小,無(wú)法取得連續(xù)的存放
多個(gè)對(duì)象的內(nèi)存塊)。因此,與上面那段C的代碼等價(jià)的 Object Pascal的代碼為:
var ptr, ptr2 : ^integer;
i : integer;
begin
GetMem(ptr, sizeof(integer) * 20);
//這句等價(jià)于C的 ptr = (int*) malloc(sizeof(int) * 20);
ptr2 := ptr; //保留原始指針位置
for i := 0 to 19 do
begin
ptr^ := i;
Inc(ptr);
end;
FreeMem(ptr2);
end;
對(duì)于以上這個(gè)例子(無(wú)論是C版本的,還是Object Pascal版本的),都要注意一個(gè)問(wèn)題,就是分配內(nèi)存的
單位是字節(jié)(BYTE),因此在使用GetMem時(shí),其第二個(gè)參數(shù)如果想當(dāng)然的寫成 20,那么就會(huì)出問(wèn)題了(
內(nèi)存訪問(wèn)越界)。因?yàn)镚etMem(ptr, 20);實(shí)際只分配了20個(gè)字節(jié)的內(nèi)存空間,而一個(gè)整形的大小是四個(gè)字
節(jié),那么訪問(wèn)第五個(gè)之后的所有元素都是非法的了(對(duì)于malloc()的參數(shù)同 樣)。
七、字符數(shù)組的運(yùn)算。C語(yǔ)言中,是沒(méi)有字符串類型的,因此,字符串都是用字符數(shù)組來(lái)實(shí)現(xiàn),于是也有
一套str打頭的庫(kù)函數(shù)以進(jìn)行字符數(shù)組的運(yùn)算,如以下代碼:
char str[15];
char *pstr;
strcpy(str, "teststr");
strcat(str, "_testok");
pstr = (char*) malloc(sizeof(char) * 15);
strcpy(pstr, str);
printf(pstr);
free(pstr);
而在Object Pascal中,有了String類型,因此可以很方便的對(duì)字符串進(jìn)行各種運(yùn)算。但是,有時(shí)我們的
Pascal代碼需要與C的代碼交互(比如:用 Object Pascal的代碼調(diào)用C寫的DLL或者用Object Pascal寫的
DLL準(zhǔn)備允許用C寫客戶端的代碼)的話,就不能使用String類型了,而必須使用兩種語(yǔ)言通用的字符數(shù)組
。其實(shí),Object Pascal提供了完全相似C的一整套字符數(shù)組的運(yùn)算函數(shù),以上那段代碼的Object Pascal
版本是這樣的:
var str : array [1..15] of char;
pstr : PChar; //Pchar 也就是 ^Char
begin
StrCopy(@str, 'teststr'); //在C中,數(shù)組的名稱可以直接作為數(shù)組首地址指針來(lái)用
以前學(xué)C++,發(fā)現(xiàn)C++最強(qiáng)大的地方就是指針了(這個(gè)不是我發(fā)現(xiàn)的。。。大牛都這么說(shuō),我只是小菜一個(gè)),寫東西的時(shí)候總是用指針,而且C++的很多函數(shù)參數(shù)都是用的指針,特別是需要輸出的參數(shù),幾乎都是用的指針傳址,從而得到返回值,我發(fā)現(xiàn)很多API函數(shù)的指針類型參數(shù)都是[out]的說(shuō)明。
用了那么就的指針,自己也算是小小的弄明白了,指針就是用來(lái)尋址的(大牛別扔西瓜啊。。。),編程里面難免會(huì)遇到類型強(qiáng)制轉(zhuǎn)換,而指針用的強(qiáng)制轉(zhuǎn)換特別多,而我個(gè)人認(rèn)為指針類型的強(qiáng)制轉(zhuǎn)換很大一個(gè)原因是需要對(duì)地址進(jìn)行尋址,不同的指針類型只是說(shuō)尋址的時(shí)候的步長(zhǎng)不同而已,別的沒(méi)什么區(qū)別。舉個(gè)例子來(lái)說(shuō):
一個(gè)short的指針*ps和char的指針*pc,假設(shè)都指向地址1000,假設(shè)地址單位為字節(jié),對(duì)于加一來(lái)說(shuō),ps+1就是說(shuō)地址向后移動(dòng)了一個(gè)字,也就是說(shuō)加完之后指向1002,而對(duì)于pc+1來(lái)說(shuō),后移一個(gè)字節(jié),結(jié)果便是1001了。而對(duì)于強(qiáng)制轉(zhuǎn)換來(lái)說(shuō),只是改變尋址步長(zhǎng)。
short *ps;
char *pc;
pc+1;//1001
ps+1;//1002
同樣,上面的(char*)ps+1,得到的結(jié)果便是1001,而(short*)pc+1結(jié)果是1002。這個(gè)便是指針強(qiáng)制轉(zhuǎn)換所帶來(lái)的好處了。
short *ps;
char *pc;
(short*)pc+1;//1002
(char*)ps+1;//1001
最近在學(xué)Delphi,剛開(kāi)始學(xué)那些組件,然后在delphi中用API函數(shù),用到API函數(shù)的時(shí)候,通過(guò)函數(shù)聲明,發(fā)現(xiàn)了不同點(diǎn),C++的API說(shuō)明中的指針參數(shù)很大一部分在Delphi中被換成了var的引用(萬(wàn)一大師說(shuō)的這其實(shí)也是傳址),然后就看了點(diǎn)Delphi指針的說(shuō)明,說(shuō)Delphi也有指針,自己也學(xué)習(xí)了下Delphi的指針,總結(jié)一下:
Delphi的指針本質(zhì)上和C++沒(méi)什么區(qū)別,都是地址,但是Delphi的指針的算數(shù)運(yùn)算就沒(méi)C++那么的靈活了(Delphi的指針更加嚴(yán)格),對(duì)于“+”加來(lái)說(shuō),只支持PChar和整數(shù)相加(這個(gè)是在Object Pascal里面看到的),也就是說(shuō)對(duì)于“+”和“-”操作來(lái)說(shuō),步長(zhǎng)是一個(gè)字節(jié),同樣加1的話例:
var
pshort:ShortInt;
psmall:SmallInt;
//同樣假設(shè)初始指向1000,單位字節(jié)
begin
PChar(pshort)+1;//結(jié)果為1001
PChar(psmall)+1;//結(jié)果也1001
end;
要想實(shí)現(xiàn)C++一樣的功能的話:
var
pshort:ShortInt;
psmall:SmallInt;
//同樣假設(shè)初始指向1000,單位字節(jié)
begin
PChar(pshort)+1;//結(jié)果為1001
PChar(psmall)+sizeof(SmallInt)*1;//結(jié)果為1002,我這樣寫只是為了形象,PChar(psmall)+2;就OK了
end;
若想實(shí)現(xiàn)和C++一樣的強(qiáng)制轉(zhuǎn)換來(lái)改變指針步長(zhǎng)的話,便得這么寫:
var
pshort:ShortInt;
psmall:SmallInt;
//同樣假設(shè)初始指向1000,單位字節(jié)
begin
PChar(pshort)+sizeof(SmallInt)*1;//結(jié)果為1002
PChar(psmall)+1;//結(jié)果也1001
end;
這個(gè)好像沒(méi)什么特別,反正Delphi都是將步長(zhǎng)轉(zhuǎn)為字節(jié)來(lái)算數(shù)加減的,所以這種強(qiáng)制轉(zhuǎn)換沒(méi)意義。
不過(guò)Delphi提供的一個(gè)函數(shù)Inc能實(shí)現(xiàn)除了PChar類型指針的自加尋址,這個(gè)就是你的指針類型是什么類型,步長(zhǎng)便是此指針類型的大小,例:
var
pi:Pinteger;
psmall:PSmallInt;
//同樣假設(shè)初始地址1000,單位字節(jié)
begin
inc(pi);//1004
inc(psmall);//1002
inc(psmall, 2);//1006
end;
這些就是小弟作為一個(gè)菜鳥(niǎo)對(duì)指針的理解,當(dāng)然這只是指針中的一小部分,要學(xué)的還很多,這個(gè)也只是寫出來(lái)和菜鳥(niǎo)同志們互相交流,供大牛指點(diǎn)的。