前言:筆者本來是打算在上一篇文章中,把多維數(shù)組和多維指針與數(shù)組的訪問方式結(jié)合起來一起寫掉。但是在寫作過程中,發(fā)現(xiàn)創(chuàng)作篇幅明顯過長,不利于日后按圖索驥,所以又重開了一篇,說說自己的感悟,僅供參考
多維數(shù)組和多維指針
1.一維數(shù)組和多維數(shù)組的存儲區(qū)別
??假設(shè)我們現(xiàn)在有如下代碼分別聲明了一個一維數(shù)組和一個二維數(shù)組:
int array1[3];
int array2[3][3];
-
一維數(shù)組的存儲
??我們假設(shè)此代碼編譯運行在32位機上,一個int類型數(shù)據(jù)占4個字節(jié),array1數(shù)組從地址0x5000_0000處開始分配空間,則可得array1數(shù)組的存儲框圖如下:
在這里插入圖片描述
??在上圖中使用一個 ?? 符號代表一個未知具體數(shù)值的字節(jié)數(shù)據(jù),array1中的3個int類型的數(shù)據(jù)其實實際所占字節(jié)數(shù)為12。作為數(shù)組元素連續(xù)存儲,因此從數(shù)組首地址0x5000_0000向后地址遞增存放數(shù)據(jù),其實際所占地址為0x5000_0000至0x5000_000B。 -
多維數(shù)組的存儲
??多維數(shù)組中,以二維數(shù)組為代表重點闡述,其余三維四維乃至N維可以以此類推。在C語言中,多維數(shù)組的存儲是以行序為主,下面的闡述也遵循這個規(guī)律。
??我們假設(shè)array2數(shù)組的首地址是0x6000_000C,其后按地址連續(xù)存儲,則有如下存儲框圖:
在這里插入圖片描述
??從上圖可以看出,該數(shù)組含有9個int類型元素,共占36個字節(jié),從首地址開始連續(xù)存儲分配。 總結(jié):
1)任何數(shù)組(不論是一維數(shù)組還是多維數(shù)組),其數(shù)組首地址都是一個和數(shù)組名直接關(guān)聯(lián)的地址常量。
??即,如上述array1和array2等數(shù)組名直接和數(shù)組首地址關(guān)聯(lián),它們可以出現(xiàn)在賦值符號=的右邊,作為一個地址量賦給指針,但是不能出現(xiàn)在=的左邊,對其重新進行賦值。其首地址的常量值,在代碼編譯鏈接時期,由編譯器和鏈接器確定,整個程序運行期間不能被更改。
2)數(shù)組元素是以順序方式連續(xù)存儲的,這意味著,我們可以通過一個首地址和一個偏移量定位到數(shù)組中的每個元素。
??每次操作數(shù)組中的元素時,不管是通過下標(biāo)引用還是指針訪問的方式,都是先從數(shù)組的首地址開始,加減一個偏移量,找到該元素的地址,再對該元素進行操作。
2.一維指針和多維指針
??一維指針和多維指針沒有特別需要闡述的地方,它們之間的區(qū)別主要是:一維指針僅僅進行了單次地址轉(zhuǎn)換即可以取出數(shù)據(jù)操作;而多維指針則根據(jù)聲明的維數(shù)需要進行多次地址轉(zhuǎn)換才能夠取到目標(biāo)數(shù)據(jù)。但是,指針作為一個數(shù)據(jù)變量,可以多次賦值,使得其成為對數(shù)組操作訪問的一大利器,所以指針和數(shù)組的結(jié)合才是重中之重。
3.數(shù)組指針和指針數(shù)組
-
3.1 數(shù)組指針
??數(shù)組指針,顧名思義就是一個指向數(shù)組的指針。一維指針可以指向一個同類型的一維數(shù)組,但多維指針不一定可以直接指向一個多維數(shù)組。有如下聲明:
int vector[10],*vp = vector;
int matrix[3][10],*mp = matrix;
??在上述聲明中,vector是一個含有10個元素的數(shù)組,每個元素都是int類型,vector作為數(shù)組首地址,其類型是一個int的地址,因此可以賦值給int *類型的變量vp。
??而在二維數(shù)組的聲明中,結(jié)合行序優(yōu)先的規(guī)律看,其實是先聲明了一個數(shù)組matrix[3],含有三個元素,每個元素是int [10] 類型, matrix作為數(shù)組首地址,存儲的 matrix[0] 元素的類型也是 int [10]類型,與被聲明為int * 類型的mp類型不符合,不能被賦值。
??若想要聲明一個指向matrix的指針,則應(yīng)該如下聲明:
int (*p)[10];
之前說到,下標(biāo)引用 [ ]的優(yōu)先級大于指針引用*,但若出現(xiàn) ()則,其優(yōu)先級最高。在上述聲明中,先看 ( * p) 聲明了一個指針,余下的int [10],則是其指向的數(shù)據(jù)類型。因此,matrix可以賦給p,它們的數(shù)據(jù)類型相同。而 matrix[0] 則可以賦給mp,它們的數(shù)據(jù)類型都是int *。
-
3.2 指針數(shù)組
??指針數(shù)組,其本質(zhì)是一個數(shù)組,只不過其元素類型都是指針類型。如下所示:
int *a[10];
下標(biāo)引用的優(yōu)先級高于間接訪問,所以在這個表達(dá)式中首先執(zhí)行下標(biāo)引用,可以得出a是一個含10個元素的數(shù)組,其元素類型為int *。其數(shù)據(jù)類型與上述vector相同,因此 a[0] = vector 成立。
3.數(shù)組指針的數(shù)組和多維數(shù)組的關(guān)聯(lián)
??讓我們回到一開始的多維數(shù)組,在腦海中重新組織我們對數(shù)組的理解。對于 int array2 [3][3],我們一般對于其最常見的理解是array2是一個數(shù)組的首地址,它含有3個元素,每個元素又都是另外一個數(shù)組的首地址,如下圖所示(靈魂手稿突現(xiàn)):
-
4.1數(shù)組指針數(shù)組
??當(dāng)我們采用數(shù)組指針數(shù)組的方式存儲這些數(shù)據(jù)時,其操作方式類似于多級索引。我們不僅要花空間存儲原來的數(shù)據(jù),還要花多余的空間存儲索引項,即這里的數(shù)組指針項。如下圖:
在這里插入圖片描述
??并且,由于數(shù)組指針已經(jīng)存儲了實際數(shù)據(jù)數(shù)組的索引,所以實際的數(shù)組可以采用數(shù)組間離散存儲的方式,其每個數(shù)組的大小也可以不相等。 -
4.2多維數(shù)組存儲
??采用多維數(shù)組存儲數(shù)據(jù)時,由于其子數(shù)組大小相等,都采用順序存儲方式存儲數(shù)據(jù),每個子數(shù)組的首地址可以通過二維數(shù)組的首地址+偏移量計算所得,所以不需要采用額外的空間存儲子數(shù)組首地址的索引。如下圖所示:
在這里插入圖片描述 總結(jié):
??我們可以將多維數(shù)組的第一維看作是一個數(shù)組指針,其存放的指針地址指向其隨后的每一維子數(shù)組,但實際存儲上,二者有很大差別:
1)多維數(shù)組必須順序存儲,大小固定,所以其子數(shù)組的首地址是常量,可通過二維數(shù)組首地址+偏移量計算所得,不需要占用額外的存儲空間。
2)數(shù)組指針數(shù)組需要占用額外的存儲空間記錄子數(shù)組的首地址,因此子數(shù)組大小可變,也可以采用離散存儲方式,更加靈活。
3)關(guān)于二種方式的應(yīng)用場景:
??如果存儲的數(shù)據(jù)大小比較接近緊湊,建議聲明為多維數(shù)組會更加節(jié)省存儲空間,操作方便;若數(shù)據(jù)集合中,數(shù)據(jù)多為長短不一且最長數(shù)據(jù)和最短數(shù)據(jù)相差較大,例如:字符串存儲,建議聲明為數(shù)組指針數(shù)組會更加節(jié)省空間。
5.作為函數(shù)參數(shù)的數(shù)組和指針
??函數(shù)調(diào)用傳參的過程都是都先將實際參數(shù)復(fù)制給形式參數(shù)。作為數(shù)組傳參是將數(shù)組的首地址傳給形式參數(shù),作為指針是將指針變量的內(nèi)容傳入。
-
5.1一維數(shù)組和一維指針的函數(shù)原型
void fun (int * v); //一維指針
void fun (int v [ ]); //一維數(shù)組
??上面兩個函數(shù)的聲明功能相等,其傳入形參的類型都是int類型的地址。對于數(shù)組當(dāng)形參的聲明來說,編譯器并不關(guān)心數(shù)組有多少個元素(即數(shù)組下標(biāo)越界是代碼編寫者需要關(guān)注的事),編譯器只關(guān)心數(shù)組的首地址。因此上述形參中數(shù)組下標(biāo)[ ]中的數(shù)值缺省。 -
5.2多維數(shù)組和多維指針的函數(shù)原型
void fun (int( * v)[10]); //數(shù)組指針
void fun (int v [ ][10]); //二維數(shù)組
??上述兩個函數(shù)聲明的功能相等,其都傳入了一個首地址,且該首地址都指向一個int [10]數(shù)據(jù)類型。值得注意的是,數(shù)組作形參時,其第一維長度必須省略,其后所有維度的長度必須列出。
- Q:為什么數(shù)組除第一維長度外的所有長度都不能省略?
-
A: 當(dāng)數(shù)組作為形參時,我們需要傳入其數(shù)組首地址,然后對首地址進行加減運算以得到下一個數(shù)據(jù)地址,在第一個例子中,
int類型占4個字節(jié),所以每次對地址進行加減時,都是以4為步進單位。第二個例子中,int [10]類型占40個字節(jié),所以對傳入地址的加減步進值為40.除第一維外的所有維數(shù)的長度是為了確定該數(shù)組首地址的加減步進值,因此不能省略。
?而有時可以省略第一維長度的場合,比如:
?int a [ ][2] = { {1,2},{3,4}};
?編譯器會根據(jù)初始化內(nèi)容自動判定數(shù)組的第一維長度為2.因此,在某些情況下,數(shù)組的第一維長度可省略。
