第五章 指向數組的指針

 

講到第五章了,數組兩個字還離不開我們的左右,數組的內容也真多,另一方面也因爲數組與指針的關係的確非常密切。

        通常,對於int a[8][9]這個二維數組,我們可以這樣定義一個指向它的指針:

int (*p)[9];

這個聲明的形式跟人們所熟悉的int *p的形式大相庭徑,初學者通常會感到迷惑,不理解的地方大致有四個:

1。爲什麼會以這種形式聲明?

2。(*p)應該如何理解?

3。爲什麼必須把第二維顯式地聲明?

4。爲什麼忽略第一維?

下面我們就一起逐個討論這四個問題:

1。這種形式是C標準的聲明語法規定的,由於本章不是對標準的解釋,只是對標準的應用,因此筆者儘量以簡潔的方式解釋這個聲明,詳細的討論將在第七章進行。C標準的聲明包含了兩部分:

聲明:

聲明說明符  初始化聲明符表opt      (opt的意思是可選)

在聲明說明符裏面有一項類型說明符,int就是這種類型說明符。而初始化聲明符表裏面的其中一種形式,就是:

直接聲明符 [常量表達式opt]

(*p)[9]就是這種直接聲明符加[]的形式。

2。p左邊的*在這裏不是取值運算符,而是一個聲明符,它指出p是一個指針。而()括號是不能去掉的,如果去掉了,由於[]運算符優先級比*高,p就會先跟[]結合,這樣p就變成了一個指針數組,而不是指向數組的指針。

題外話:

*p還有一種用法,就是當*是取值運算符的時候,*p是一個左值,表示一個變量,爲什麼*p是一個變量呢?也許有人會說,因爲int i, *p=&i嘛,其實這是結果不是原因。嚴格來說,i只是一個變量名,不是變量,在編譯器的符號表裏面,變量名是一個符號地址,它所代表的地址值是它指向的那段內存單元的地址,真正叫變量的是那段內存單元,懂彙編的朋友能很容易地區分出來,在彙編裏面,可以這樣定義一個變量名:

VARW  DW  10,20

VARW就是一個變量名,它在彙編裏面是一個地址,代表了10所在的內存單元這個變量。由於p被初始化爲&i,*p指向i所代表的那段內存單元,因此說*p是一個變量。把i稱爲變量是一種習慣上的統稱。

3。定義一個指針的時候,首先必須定出指針的類型,由於這是一個指向數組的指針,如果數組的元素的類型定下來了,那麼這個指針的類型也就定下來了。前面說過,C語言的多維數組實質上是數組的嵌套,那麼所指向數組的元素必定具有數組類型,也就是說,這個數組的元素是一個具有6個int元素的數組,因此,p定義的時候,必須指定第二維的上界,這樣才能把p的類型定下來。

4。有這種疑問的人已經犯了一個錯誤,沒有分清楚什麼是指針,什麼是數組,以數組的思維模式來看待這個指針p。定義一個數組(非static)的時候,需要在棧中靜態分配一塊內存,那麼就需要知道這塊內存的大小,因此定義數組時需要確定各維的上界。而這裏只是定義一個指針而已,對於一個指針的定義,需要知道的是它所指向對象的類型,並不需要知道對象的大小,這是多餘的。因此,所有指向數組的指針的第一維被忽略。

        以上介紹瞭如何聲明一個指向二維數組的指針,類似地,對一個指向n維數組的指針也可以用同樣的方法來聲明,如下:

int (*p)[x2][x3]......[xn];

同樣可以忽略第一維,而其它維必須指定上界。

        最後再討論一種很常見的對多維數組的錯誤理解,有些人常常會以爲,二維數組就是二級指針,這種錯誤的根源,來自於可以把一個二級指針int **p以p[i][j]這種形式使用。首先把數組稱爲指針就是錯誤的,第一章筆者已經說明了數組名是地址,不能理解爲指針。第二,並非能以p[i][j]這種形式使用,那麼p就是一個二維數組了,C標準對數組引用的規定,並沒有指定數組引用時[]運算符的左邊必須是數組名,而可以是一個表達式。第三,這是一種“巧合”,歸根到底是由於C語言的數組實現是數組的嵌套同時C標準把[]運算符轉換爲類似*(*(a+i)+j)這樣的等價表達式造成的,那兩個取值運算符“恰好”可以用於一個二級指針。第四,p與p[i]並不具有數組類型,sizeof(p)和sizeof(p[i])的結果只是一個指針的大小4字節。而對於一個真正的數組,p與p[i]都是具有數組類型的地址。

        實際上,int **p只是一個指向一維指針數組的指針,而不是指向二維數組的指針。同樣地,對於n級指針,都可以看作一個指向一維指針數組的指針,這個指針數組的元素都是n-1級指針。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章