指針是c語言的精髓,但是很多初學者往往對于指針的概念并不深刻,以至于學完之後随着時間的推移越來越模糊,感覺指針難以掌握,本文通過簡單的例子試圖将指針解釋清楚,今天的重點有幾個方面:
<a href="http://www.cnblogs.com/kenshincui/p/3848442.html#pointdefinations">什麼是指針</a>
<a href="http://www.cnblogs.com/kenshincui/p/3848442.html#arrayandpoint">數組和指針</a>
<a href="http://www.cnblogs.com/kenshincui/p/3848442.html#functionpoint">函數指針</a>
存放變量位址的變量我們稱之為“指針變量”,簡單的說變量p中存儲的是變量a的位址,那麼p就可以稱為是指針變量,或者說p指向a。當我們通路a變量的時候其實是程式先根據a取得a對應的位址,再到這個位址對應的存儲空間中拿到a的值,這種方式我們稱之為“直接引用”;而當我們通過p取得a的時候首先要先根據p轉換成p對應的存儲位址,再根據這個位址到其對應的存儲空間中拿到存儲内容,它的内容其實就是a的位址,然後根據這個位址到對應的存儲空間中取得對應的内容,這個内容就是a的值,這種通過p找到a對應位址再取值的方式成為“間接引用”。這裡以表格形式列出a和p的存儲以幫助大家了解上面說的内容:

接下來,看一下指針的指派
需要說明兩點:
a.int *p;中的*隻是表示p變量是一個指針變量;而列印*p的時候,*p中的*是操作符,表示p指針指向的變量的存儲空間(目前存儲就是1),同時我們也看到了*p==a;修改了*p也就是修改了p指向的存儲空間的内容,也就修改了a,是以第二次列印a=2;
b.指針所指向的類型必須和定義指針時聲明的類型相同;上面指針q定義成了int型而指向了char型,結果輸出*q列印出了2049,具體原因見下圖(假設在16位編譯器下,指針長度為2位元組)
由于局部變量是存儲在棧裡面的,是以先存儲b再存儲a、p,當列印*p的時候,其實就是以p指向的位址對應的空間開始取兩個位元組的資料(因為定義p的時候它指向的是int型,在16位編譯器下int類型的長度為2),剛好定義的b和c空間連續,是以就取到b的其中一個位元組,最後*p二進制存儲為“0000100000000001”(見上圖黃色背景内容),十進制表示就是2049;
c.指針變量占用的空間和它所指向的變量類型無關,隻跟編譯器位數有關(準确的說隻跟尋址方式有關);
由于數組的存儲是連續的,數組名就是數組的位址,這樣一來數組和指針就有着很微妙的關系,先看以下例子:
<a href="http://11011.net/software/vspaste"></a>
從上面的例子我們可以得出如下結論:
數組名a==&a[0]==*p;
如果p指向一個數組,那麼p+1指向數組的下一個元素,同時注意p+1移動的長度并不固定,具體需要根據p指向的資料類型而定;
指針可以寫成p++形式,但是數組名不可以,因為數組名是常量
不管函數的形參為數組還是指針,實參都可以使用數組名或指針;
由于在c語言中字元串就是字元數組,下面不妨看一下字元串和數組的關系:
以上代碼中注釋基本已經很清楚了,這裡需要指出是為什麼printf(a)能夠直接輸出字元串呢?
我們看一下printf()的定義:int printf(const char * __restrict, ...) __printflike(1, 2);
其實printf的參數要求是指向字元類型的指針,而結合上面的例子和我們之前說的,如果函數形參是指針類型那麼可以傳入函數名,是以也就能正确輸出字元串的内容了。類似的還有上一篇文章中說的strcat()、strcpy()等函數均是如此。
在弄清函數指針的問題之前,我們不妨先來看一下傳回指針類型資料的函數,畢竟指針類型也是c語言的資料類型,下面以一個字元串轉換為大寫字元的程式為例,在這個例子中不僅可以看到傳回值為指針類型的函數同時還可以看到前面說到的指針移動操作:
大家都是知道函數隻能有一個傳回值,如果需要傳回多個值,怎麼辦,其實很簡單,隻要将指針作為函數參數傳遞就可以了,在下面的例子中我們再次看到指針作為參數進行傳遞。
函數也是在記憶體中存儲的,當然函數也有一個起始位址(事實上函數名就是函數的起始位址),這裡同樣需要弄清函數指針的關系。函數指針定義的形式:傳回值類型 (*指針變量名)(形參1,形參2),拿到函數指針其實我們就相當于拿到了這個函數,函數的操作都可以通過指針來完成,而且通過前面的例子可以看到指針作為c語言的資料類型,可以作為參數、作為傳回值,那麼當然函數指針同樣可以作為函數的參數和傳回值:
函數指針可以作為函數參數進行傳遞,實在太強大了,是不是想起了c#中的委托?記得c#書籍中經常提到委托類似于函數指針,其實說的就是上面的情況。需要注意的是,普通的指針可以寫成p++進行移動,而函數指針寫成p++并沒有意義。