之前對數組的概念一直沒有理解透徹,只覺得數組名就是個常量指標而已,用法和基本的指標差不多。所以當我嘗試用二級指標去訪問二維數組時,就經常會出錯。下面就是剛開始寫的一個錯誤的程式:
#include <stdio.h>int main(){int iArray[2][3] = {{1,2,3},{4,5,6}};int **pArray = NULL;pArray = iArray;printf("array[0][0] = %d\n", pArray[0][0]);printf("array[1][2] = %d\n", pArray[1][2]);return 0;}
開始的時候我是這樣分析的:本來數組和指標就差不多,一維數組和一維指標對應,那麼二維數組名應該和二維指標差不多,所以上面那個程式是沒有錯的,應該打 印出的是1和6。但是當我實際編譯啟動並執行時候,卻出現了段錯誤,也就是我訪問了不該訪問的地址空間。那錯誤到底出在什麼地方呢?正確的程式應該怎麼寫呢?
為瞭解決問題,不得不讓我重新理解數組的含義。仔細翻閱一些書籍後,我發現其實數組並不是我原來想象的那麼簡單:一個常量指標標識的一群變數的集合。數組 應該也算是一個完備的變數類型:有名字,有大小,也有地址。只不多就是名字和它的地址一樣罷了。也正是因為數組有大小,所以當用sizeof對數組名進行 運算時,算出來的是實際數組的大小,而不是指標的大小。
也正是因為這樣,所以指向數組的指標和指向指標的指標也大不一樣。它們倆最明顯的不同就是表現在指標步進的時候。我們知道指標在進行++運算的時候,跨越 的實際地址取決於指標指向的資料類型:對於一般的32位機來說,假如指向的是int型資料,跨越的實際地址就是4,指向的是指標型資料,跨越的實際地址也 是4,當指向的是數群組類型的時候,跨越的實際地址就是數組的長度了。
現在再回頭分析上面那個錯誤程式,根據下標引用符號[]的運算規則,我們知道pArray[0][0]其實就是**pArray,而iArray實際上只 是個陣列變數名,而它的值就是整個數組的開始地址(其實&iArray,iArray,iArray[0]以及&iArray的值都是數 組的開始地址,都是在編譯過程中編譯器賦予的值)。那麼其實*pArray就已經是iArray[0][0]的值了,也就是1,而**pArray則是去 訪問地址為1的地址空間中的資料,自然會出段錯誤。
其實用指標訪問二維數組可以直接用一級指標就可以了。比如下面這個程式:
int main(){int iArray[2][3] = {{1,2,3},{4,5,6}};int *pArray = NULL;pArray = iArray;printf("array[0][0] = %d\n", *pArray);printf("array[1][2] = %d\n", *(pArray + 1 * 3 + 2));return 0;}
因為數組本身在地址空間中就是連續排列的,根據行數和列數,我們自己計算出訪問單元的地址位移量就可以用一級指標輕鬆遍曆二維數組中的所有資料了。
我們還可以嘗試用指向數組的指標來訪問二維數組的成員。下面就是案例程式:
int main(){int iArray[2][3] = {{1,2,3},{4,5,6}};int (*pArray)[3] = NULL;pArray = iArray;printf("array[0][0] = %d\n", pArray[0][0]);printf("array[1][2] = %d\n", pArray[1][2]);return 0;}
簡單分析一下這個程式:我們知道[]運算子的結合方向是由左向右,pArray[1][2]就等價於(* (pArray + 1))[2],而由於pArray是數組指標,而且數組的長度為3,所以* (pArray + 1)就表示iArray[1]這個數組,則pArray[1][2]則就完全等價於iArray[1][2]。
如果非得想用二級指標來訪問二維數組的話,我們還得借用指標數組(數組記憶體儲的都是指標類型的資料),下面是案例程式:
int main(){int iArray[2][3] = {{1,2,3},{4,5,6}};int *ipArray[2] = {iArray[0], iArray[1]};int **pArray = NULL;pArray = ipArray;printf("array[0][0] = %d\n", pArray[0][0]);printf("array[1][2] = %d\n", pArray[1][2]);return 0;}
由於二級指標要跳兩次,所以中間還需要額外的儲存一級指標的空間。所以一般不建議用二級指標去訪問二維數組。