文章目錄
- 多維數組
- 數組名
- 下標
- 指向數組的指針
- 作為函數參數的多維數組
- 指針數組
- 小結
多維數組
如果某個數組的維數超過1,它就被稱為多維數組,例如,下面這個聲明:
int matrix[6][10]
創建了一個包含60個元素的矩陣。但是,它是6行每行10個元素,還是10行每行6個元素?
為了回答這個問題,你需要從一個不同的視點觀察多維數組。考慮下列這些維數不斷增加的聲明:
int a;
int b[10];
int c[6][10];
int d[3][6][10];
a是個簡單的整數,接下來的那個聲明增加一個維數,所以b就是一個向量,它包含10個整形元素。
c只是在b的基礎上再增加一維,所以我們可以把c看做是一個包含6個元素的向量,只不過它的每個元素本身是一個包含10個整形元素的向量。換句話說,c是個一維數組的一維數組。d也是如此,它是一個包含三個元素的數組,每個元素都是包含6個元素的數組,而這6個元素中的每一個都是包含10個整形元素的數組,間接地說,d是一個3排6行10列的整形三維數組。
·
數組名
一維數組名的值是一個指針常量,它指向數組的第一個元素,它的類型是“指向元素類型的指針” 。多維數組也差不多簡單,唯一的區別是多維數組第1維的元素實際上是另一個數組。例如,下面這個聲明:
int matrix[3][10];
創建了matrix,它可以看做是一個一維數組,包含3個元素,只是每個元素恰好是包含10個整形元素的數組。
matrix這個名字的值是一個指向它第一個元素的指針,所以matrix是一個指向一個包含10個整型元素的數組的指針。
下標
如果要標識一個多維數組的某個元素,必須按照與數組聲明時相同的順序為每一維都提供一個下標,而且每個下標都單獨位于一對方括號內。在下面的聲明中:
int matrix[3][10];
表達式matrix[1][5]
訪問下面這個元素
但是,下標引用實際上只是間接訪問表達式的一種偽裝形式,即使在多維數組中也是如此,考慮下面這個表達式:
matrix
它的類型是“指向包含10個整型元素數組的指針”,它的值是
表達式 matrix+1也是一個“指向10個整型元素數組的指針”,但它指向matrix的另一行
為什么?因為1這個數值根據包含10個整型元素的數組的長度進行調制,所以它指向matrix下一行,如果對其執行間接訪問,就如下圖隨箭頭選擇中間的這個子數組
所以表達式*(matrix+1)
事實上標識了一個10個整型元素的子數組。數組名的值是個常量指針,它指向數組的第一個元素,在這個表達式中也是如此。它的類型是“指向整型的指針”,我們現在可以在下一維的上下文環境中顯示它的值。
現在請拿穩你的帽子,猜猜下面這個表達式的結果是什么?
*(*(matrix+1)+5)
它所訪問的正是那個整型元素。如果它作為右值使用,你取得存儲于那個位置的值,如果它作為左值使用,這個位置將存儲一個新值。
這個看上去嚇人的表達式實際上正是我們的老朋友–下標,我們可以把表達式*(matrix+1)改寫成matrix[1],把這個下標表達式帶入原先的表達式,我們將得到:
*(matrix[1]+1);
這個表達式完全合法的,matrix[1]選定一個子數組,所以它的類型是一個指向整型的指針,我們對這個指針加上5,然后執行間接訪問操作。
但是,我們可以再次用下標代替間接訪問,所以這個表達式還可以寫出:
matrix[1][5]
指向數組的指針
下面這些聲明合法嗎?
int vector[10],*vp = vector;
int matrix[3][10],*mp = matrix;
第一個聲明是合法的。它為一個整型數組分配內存,并把vp聲明一個指向整型的指針,并把它初始化為指向vector數組的第一個元素。vector和vp具有相同的類型:指向整型的指針。但是第2個是非法的。它正確地創建了matrix數組,并把mp聲明為一個指向整型的指針。但是mp的初始化是不正確的,因為matrix并不是一個指向整型的指針,而是一個指向整型數組的指針。我們應該怎樣聲明一個指向整型數組的指針呢?
int (*p)[10];
下標引用的優先級高于間接訪問,但由于括號的存在,首先執行的還是間接訪問。所以p是個指針,但它指向什么呢?
接下來執行的是下標引用,所以p指向某種類型的數組。這個聲明表達式中并沒有更多的操作符,所以數組的每個元素都是整數。
聲明并沒有直接告訴你p是什么,但推斷它的類型并不困難,當我們對它執行間接訪問操作時,我們得到的是個數組,對該數組進行下標引用操作得到的是一個整型值。所以p是一個指向整型數組的指針。
在聲明中加上初始化后是下面這個樣子:
int (*p)[10] = matrix;
它使p指向matrix的第一行。
作為函數參數的多維數組
作為函數參數的多維數組名的傳遞方式和一位數組名相同,實際傳遞的是個指向數組第一個元素的指針。但是,兩者之間的區別在于,多維數組的每個元素本身是另外一個數組,編譯器需要知道它的維數,以便為函數形參的下標表達式進行求值。這里有兩個例子,說明了它們之間的區別:
int vector[10];
...
func1(vector);
參數vector得我類型是指向整型的指針,所以func1的原型可以是下面兩種的任何一種:
void funcl(int *vec);
void func1(int vec[]);
作用于vec上面的指針運算把整型的長度作為它的調整因子。
現在讓我們來觀察一個矩陣:
int matrix[3][10];
...
func2(matrix);
這里matrix的類型是指向包含10個整型元素數組的指針。func2的原型應該是怎樣的呢?你可以使用下面兩種形式中的任何一種:
void func2(int (*p)[10];
void func2(int mat[][10]);
在這個函數中,mat的第一個下標根據包含10個元素的整型數組的長度進行調整,接著第2個下標根據整型的長度進行調整,這和原先的matrix數組一樣。
在編寫一維數組形參的函數原型時,你既可以把它寫成數組的形式,也可以把它寫成指針的形式。但是,對于多維數組,只有第1維可以進行如此選擇。尤其是,把func2寫成下面這樣的原型是不正確的:
void func2(int**mat);
這個例子把mat聲明為一個指向整型指針的指針,它和指向數組的指針并不是一回事。
指針數組
除了類型之外,指針變量和其他變量很相似,正如你可以創建整型數組一樣,你也可以聲明指針數組。這里有一個例子:
int *api[10];
為了弄清楚這個復雜的聲明,我們假設它是一個表達式,并對它進行求值。
下標引用的優先級高于間接訪問,所以在這個表達式中,首先執行下標引用。因此,api是某種類型的數組,元素個數為10。在取得一個數組元素之后,隨機執行的是間接訪問操作,這個表達式不再有其他操作,所以它的結果是一個整型值。
那么api到底是什么東西?對數組的某個元素執行間接訪問操作后,我們得到一個整型值,所以api肯定是個數組,它的元素類型是指向整型的指針。
小結
一維數組的數組名指向第一個元素,類型是指向元素類型的指針。
二維數組的數組名是也指向它第一個元素,類型是指向數組的指針。
指針的指針是指向某種類型指針的指針,它和指向數組的指針并不是一回事。