指針是C語言的一個(gè)核心特色,它以一種統(tǒng)一方式對不同數(shù)據(jù)結(jié)構(gòu)中的元素產(chǎn)生引用。對于新手來說,指針總是會(huì)帶來很多困惑,但其實(shí)指針的基本概念非常簡單。下面是一些指針和它們映射到機(jī)器代碼的關(guān)鍵原則。
1、每個(gè)指針都對應(yīng)一個(gè)類型
指針類型表明指針指向的是哪一類對象。比如:
int *ip;
char **cpp;
變量ip是一個(gè)指向int類型對象的指針,而cpp指針指向的對象自身就是一個(gè)指向char類型對象的指針。
通常,如果對象類型是T,那么指針的類型為T*。特殊的void *類型代表通用指針。比如malloc函數(shù)返回一個(gè)通用指針,然后通過顯式強(qiáng)制類型轉(zhuǎn)換或賦值操作的隱式強(qiáng)制類型轉(zhuǎn)換,將它轉(zhuǎn)換成一個(gè)有類型的指針。
指針類型不是機(jī)器代碼中的一部分,它是C語言提供的一種抽象,幫助程序員避免尋址錯(cuò)誤。
2、每個(gè)指針都有一個(gè)值
指針的值是某個(gè)指定類型的對象的地址。特殊的NULL(0)值表示該指針沒有指向任何地方。
3、用“&”創(chuàng)建指針
“&”運(yùn)算符可以應(yīng)用到任何value類的C表達(dá)式上,value類指可以出現(xiàn)在賦值語句左邊的表達(dá)式,包括變量、結(jié)構(gòu)和數(shù)組的元素。
4、用“*”間接引用指針
“*”運(yùn)算符用于間接引用指針,其結(jié)果是一個(gè)值,它的類型與該指針的類型一致。間接引用是用內(nèi)存引用來實(shí)現(xiàn)的,要么是存儲(chǔ)到一個(gè)指定的地址,要么是從指定的地址讀取。
5、數(shù)組與指針緊密聯(lián)系
一個(gè)數(shù)組的名字可以像一個(gè)指針變量一樣引用(但不能修改)。數(shù)組引用(a[3])與指針間接引用(*(a+3))有一樣的效果。
數(shù)組引用和指針運(yùn)算都需要用對象大小對偏移量進(jìn)行伸縮。如表達(dá)式 p+i,這里的指針p的值為p,得到的地址計(jì)算為 p+L*i,這里的L是與p相關(guān)聯(lián)的數(shù)據(jù)類型的大小。
6、指針的類型轉(zhuǎn)換改變指針運(yùn)算的伸縮
將指針從一種類型強(qiáng)制轉(zhuǎn)換成另一種類型,只改變它的類型,而不改變它的值。強(qiáng)制類型轉(zhuǎn)換的一個(gè)效果是改變指針運(yùn)算的伸縮。
比如p是一個(gè)char *類型的指針,它的值為p,那么(int *)(p+7)的結(jié)果為 p+7,而(int *)p+7的計(jì)算結(jié)果為p+28。
PS:強(qiáng)制類型轉(zhuǎn)換的優(yōu)先級(jí)高于加法。
7、指針也可以指向函數(shù)
這里指針提供了一個(gè)強(qiáng)大的存儲(chǔ)和向代碼傳遞引用的功能,這些引用可以被程序的其他部分調(diào)用。
比如有一個(gè)函數(shù)定義如下:
int fun(int x, int*p);
然后可以聲明一個(gè)指針fp,將它賦值給這個(gè)函數(shù),
int (*fp)(int,int *);
fp = fun;
然后就可以用這個(gè)指針來調(diào)用這個(gè)函數(shù):
int y = 1;
int result = fp(3, &y);
函數(shù)指針的值是該函數(shù)機(jī)器代碼表示中第一條指令的地址。
函數(shù)指針聲明的語法對新手程序員比較難以理解,對于以下聲明:
int (*f)(int*);
要從里往外讀,即從(*f)開始,f是一個(gè)指針,而(*f)(int *)表明f是一個(gè)指向函數(shù)的指針,這個(gè)函數(shù)以一個(gè) int *作為參數(shù)。最后我們看到,它是指向以 int *為參數(shù)并返回int的函數(shù)的指針。