在C/c++中,數組和指標有著密切的關係,有很多地方說數組就是指標式錯誤的一種說法。這兩者是不同的資料結構。其實,在C/c++中沒有所謂的二維數組,書面表達就是數組的數組。我猜想是為了表述方便才叫它二維數組。
在本文中,我也就叫它二維數組。在C/C++中,二維數組是數組的數組。數組的每一個元素是一個數組。說起來有點繞,大家都知道,一維數組也和指標那關比較密切,在本文中不重點闡述,下面就來闡述二維數組和指標之間到底存在著什麼樣的關係。
一、二維數組一維化
其實我這裡也只是表述的方便才叫這麼一個題目,我們怎麼利用一個數組的訪問方式來訪問二維數組呢?下面來看一個具體的例子。
首先,定義一個二維數組。
int iArr[2][3]={0,1,2,3,4,5};
我們可以用一個指向int型的指標變數來訪問這個數組,下面的代碼是將數組一維化:
int* p = iArr[0];
上面的iArr[0]就是代表第一個數組的首地址,由於二維數組在記憶體中的儲存也是先行後列的方式,所以第二行也緊跟第一行之後,這樣就可以用p來訪問數組的元素值了,訪問的方式有下標和指標方式。
printf("%d,",p[3]);printf("%d\n",*(p+3));
最後輸出的結果都是4。講完了一維化之後,下面來繼續看二維數組的函數名到底是什麼意思?
二、關於二維數組名的探索
可能想當然的話,二維數組不就是一個二級指標嗎?真是這樣嗎?下面用代碼來驗證下:
int **pp = iArr;
不出意外,會出現下面的編譯錯誤:
error C2440: “初始化”: 無法從“int [2][3]”轉換為“int **”
其實二維數組名是一個數組指標,那什麼是數組指標?數組指標是指向一個數組首地址的指標,它實際上也是一種指標類型,類似於函數指標。它聲明如下:
int (*pArr)[3]
它說明pArr是一個數組指標,它指向的是一個數組元素為int類型並且數組元素的個數為3的一個數組指標,奇怪,中間的怎麼還有一個括弧是啥玩意?呵呵,這個括弧還真是不可少的。少了它就變為另外一種類型了:指標數組。指標數組是數群組類型,代表數組的每一個元素是指標類型,它聲明如下:int *pArr[3]。
既然二維數組的數組名是指向第一行數組的首地址,我們也叫它行指標。那麼我們可以用這種數組名或者指標來訪問二維數組的元素。
int (*pArr)[3] = iArr;
下面,我要訪問第一行第二列的元素,我可以用下面的代碼來訪問
*(*(pArr+1) + 2)
也可以用數組名來訪問:
*(*(iArr+1) + 2)
這種方式是不是一下很難看懂,為什麼兩個星號啊?下面就我的理解來作一下解釋。僅以pArr做說明
首先,pArr是一個指向數組的指標,在這個指標上加減一個整數都是移動整行,而不是一個元素。比如說,pArr+1代表的現在指標已經指向第一行元素了,也就是實際中的第二行,而要取得指標所指的對象,就要用到解引用運算子*,所以*(pArr+1)就代表第一行數組,是整個這一行元素就取到了,那現在要取這一行的第二個元素,只須將指標再移動兩個元素,即*(iArr+1) + 2,這樣就指向了這個元素的地址,再解引用取得元素的值即可。說的有點囉嗦,或許有錯誤,望高手別噴就是了。
三、作為函數參數
一維數組名作為函數參數實際上是退化為指標,二維數組作為函數參數又有什麼不同呢?下面舉個例子說明。
聲明了如下函數:
void TestFun(int *pArr,int nlength)
假設,我用數組名和指向首個元素地址的指標作為傳遞參數,看看有什麼效果?
TestFun(iArr,6);//“TestFun”: 不能將參數 1 從“int [2][3]”轉換為“int *”TestFun(&iArr[0][0],6);
直接傳遞數組名是編譯通不過的。因為數組名是數組指標,而函數的參數是int*,兩者的類型化完全不一樣,所以不能轉換。
而數組首元素的地址顯然是int*類型,所以就能編譯通過。
假設,我現在把這個函數的聲明換一下,看看這兩種傳參的方法會出現什麼情況?
現在的聲明是:
void TestFun(void *pArr,int nlength)
還是這樣傳參,
TestFun(iArr,6);TestFun(&iArr[0][0],6);
編譯一下,居然都能通過了。在這裡,第二種方式顯然是沒問題的,因為int*可以轉化為void*。而第一種方式怎麼就可以了呢?因為iArr是數組指標,當然也可以轉換為void*啦。
四、後記
天快黑了,要吃飯去了。本文就寫到這裡,文中有什麼不對的地方,可以指出來,大家一起討論。