解讀 C 語言中的指針

我想對不少學習C語言的新手來講,指針無疑是一個難點。可是,我以爲指針也是C語言特別重要的一個特性。也許,你在除了C和C++之外的編程語言中,不多看到指針。而C++中,也多用引用,而非指針。指針,做爲一種高效的工具,可謂是一把雙刃劍——用得好,能夠大大提升程序效率,但用的很差,就是不少bug的滋生地。編程

這或許也是人們對指針褒貶不一的緣由吧。就我我的而言,我仍是很喜歡這個特性,由於我須要常常和硬件以及一些底層的軟件打交道。這個時候,指針便體現出它獨特的魅力。指針的知識不少,有一本經典的書叫《C和指針》,若是有興趣能夠讀一讀。這裏,我主要總結一些如何去解讀指針(說實話這個東西實在是很容易讓人困惑)的方法,一方面給本身作查詢用,另外一方面,但願能夠給別人一些幫助。數組

一,基本概念編程語言

關於指針的基本概念,我就不詳細介紹了,由於有許多書都介紹的很詳細。這裏我只介紹一部分。指針指向一個地址,而指針自己在大多數系統上都是一個無符號整數(在32bit機上是4byte,在64bit機上是8byte)。下面用一個例子來講明其機制:函數

在上面的例子中,先定義了一個指針p,它的類型是int,也就是說它只能指向一個int型的變量,而不能指向其餘類型的變量。最後咱們將a變量的地址賦給p。在這個過程當中,涉及到兩個內存塊,一個是存放指針p的內存(用&p可獲得內存地址),一個是存放a的值的內存塊(用&a能夠獲得內存地址)。而第一個內存存的p的值通過賦值語句後也就是&a的值了。另一個注意點是, *(星號)和變量類型以及變量名之間能夠有任意個空格,也能夠沒有。好比下面三種方式都是同樣的:工具

在上面的例子中,先定義了一個指針p,它的類型是int,也就是說它只能指向一個int型的變量,而不能指向其餘類型的變量。最後咱們將a變量的地址賦給p。在這個過程當中,涉及到兩個內存塊,一個是存放指針p的內存(用&p可獲得內存地址),一個是存放a的值的內存塊(用&a能夠獲得內存地址)。而第一個內存存的p的值通過賦值語句後也就是&a的值了。另一個注意點是, *(星號)和變量類型以及變量名之間能夠有任意個空格,也能夠沒有。好比下面三種方式都是同樣的:學習

解讀方法:指針

看下面一個例子:blog

二,數組首地址a,&a,&a[0]內存

注:a,&a,&a[0]的含義雖然不一樣,可是他們三個的值是相等的!編譯器

以int a[3]爲例說明:

  1. a做爲右值時,表明數組首元素的首地址,而非數組地址。 也就是a[0]的地址。int i = (a+1),這裏a是右值,因此表明首元素的首地址,a+1表明下一個元素的首地址,即&a[1]。

  2. a是整個數組的名字。因此sizeof(a)的值爲sizeof(int) * 3 = 40,表明整個數組的大小。

  3. &a即爲取a的首地址,也即整個數組的首地址。因此sizeof(&a) = 4。 int p = (int)(&a+1)中的&a+1表明下一個數組的首地址,顯然是越界的。

  4. &a[0]表明首元素的首地址。 因此sizeof(&a[0]) = 4。

  5. &a[3],很顯然數組越界了,但它的sizeof是多少呢? 也是4,由於關鍵字sizeof求值是在編譯的時候,雖然並不存在a[3]這個元素,可是這裏並無真正訪問a[3],而是根據數組元素類型來肯定其值的。因此sizeof(a[3])不會出錯。

  6. a[-1]表明什麼意思?首先要明白下標的形式被編譯器解析成指針的形式,即a[1]被解析成(a+1)。那麼,a[-1]被解析成*(a-1)。

關於數組首元素的首地址和數組的首地址的區別:其實,數組首元素的首地址和數組首地址的值是相同的,即&a[0]和a(以及&a)是相等的,可是而這含義不同。首元素的首地址加1後,是第二個元素的首地址(之因此一直說首地址,是由於有的類型存儲時會佔多個地址),但數組的首地址加1後是「下一個數組的地址」,這裏的下一個數組只是爲了說明加1時加了整個數組的大小,而不是一個元素的大小。

有一點比較容易混淆:a雖然表明整個數組,但(a+1)卻表明下一個元素的首地址,即和(&a[0]+1)同樣,下一個數組的形式爲:(&a+1)。 下面以一個程序來講明:

輸出結果:

說明(下面的行數只計算main函數內有代碼的行):

  1. 程序第1行定義了一個具備3個元素的整型數組。

  2. 第2行打印了long型的大小。由於我是64bit的,因此一個long是8byte。

  3. 第3行打印了*(a+1)的值,結果和a[1]的值相等。說明a雖然表明整個數組,但做爲右值時,的確表明首元素的首地址。

  4. 第4行輸出值爲12,是整個數組的大小。

  5. 第5行打印了一個出界元素的大小,沒有報錯,驗證了上面第5條。

  6. 第6行打印了a[-1]和*(a-1),輸出值相等。驗證了上面第6條。

  7. 第7行打印了a和&a[0],值相等。說明數組的首地址和首元素的首地址是相等的。

  8. 第8行打印了a,(a+1),(&a+1),由結果就能夠看出首元素的首地址加1是加了一個數組元素的大小,而數組首地址加1是加了一個數組的大小。

三,指針數組和數組指針

指針數組: 首先它是一個數組,數組的元素是指針,也成爲「存儲指針的數組」。

數組指針: 首先它是一個指針,它指向一個數組,也能夠理解爲「數組的指針」。 也能夠利用前面的「解讀方法」去分析。

四,函數指針和指針函數

函數指針: 指向函數的指針變量。

指針函數: 帶指針的函數,也就是返回指針的函數。

五,指針常量和常量指針

怎麼記?

  1. 能夠先把類型名去掉,而後看const離誰近,就修飾誰。

  2. 也能夠const在*左邊的爲常量指針,const在*右邊的爲指針常量。

三~五的萬能鑰匙

其實,關於「指針數組與數組指針、函數指針與指針函數、指針常量與常量指針」的判斷,有一個萬能鑰匙。那就是根據咱們強大的中文語法:前邊是修飾詞,後邊纔是主語。好比「指針數組」,前面的指針只是修飾詞,後面的數組纔是主語,因此它是一個數組。

六,野指針

野指針指沒有肯定指向的指針。形成野指針的狀況有:

1. 指針變量建立但沒有初始化。

2. 指針p被free或者delete以後,沒有置爲NULL。

相關文章
相關標籤/搜索