多維數(shù)組與指針
多維數(shù)組的數(shù)組名并不是很多網(wǎng)友描述的多級(jí)指針,我僅以二維數(shù)組作為研究對(duì)象,進(jìn)行一定的分析。
二維數(shù)組int A[M][N],可以認(rèn)為是存在M個(gè)元素的數(shù)組,且每一個(gè)元素都是長(zhǎng)度為N的int型數(shù)組,這樣就能比較清晰的理解了數(shù)組。數(shù)組名在很多情況下轉(zhuǎn)換為指針,且數(shù)組名是數(shù)組首個(gè)元素的指針,這是非常重要的概念,搞清楚了這個(gè)概念也就能夠分析其他多維的數(shù)組情況啦。
對(duì)于上面的數(shù)組A[M][N],數(shù)組名A實(shí)質(zhì)上指向數(shù)組的首個(gè)元素的指針,也就是說這時(shí)的數(shù)組名A變質(zhì)為一個(gè)指針,指向的類型為int[N],即指向的是一個(gè)數(shù)組,而不是一個(gè)簡(jiǎn)單的值,那么對(duì)數(shù)組名進(jìn)行解引用*A,就是得到這個(gè)一維數(shù)組(這是指針解引用的定義,就如同 int *p = &a, *p 實(shí)質(zhì)上是指a是一個(gè)道理),相當(dāng)于得到了一維數(shù)組名,即*A實(shí)質(zhì)上是一個(gè)數(shù)組,(*A)可以看做這個(gè)數(shù)組的數(shù)組名,這和int a[N]是同樣的道理,即:a = *A。同樣根據(jù)數(shù)組名是指向數(shù)組的首個(gè)元素的指針,可以將*A也可以被看做為指針,但此時(shí)的指針指向的是數(shù)組A[0]的首個(gè)元素,也就是指向了A[0][0],也就是說*(*A)就是A[0][0]。由此可知,二維數(shù)組并不是簡(jiǎn)單的指向指針的指針,而是每一次解引用對(duì)應(yīng)的都是不同的指針類型。
對(duì)于多維的數(shù)組,假設(shè)存在int B[N][M][S]這個(gè)三維的數(shù)組,那么數(shù)組名B是一個(gè)指針,其指向的類型是int[M][S]。*B也是指針,但是其指向的類型是int[S]。**B同樣也是指針,但是指向的類型是int型。更多維的數(shù)組也可以采用類似方法的分析。所以說并不能說是多維數(shù)組的數(shù)組名多級(jí)指針,因?yàn)槎嗑S數(shù)組對(duì)數(shù)組名的解引用操作都得到了一個(gè)不同大小的數(shù)組空間的指針,并不是指向單一元素的指針。
由上面的分析可知,二維數(shù)組名A是一個(gè)指向一維數(shù)組的指針,指針的加法是指在當(dāng)前地址上加上類型的長(zhǎng)度,得到指向的新地址,這里的類型就是數(shù)組int[N]。因此A+i實(shí)質(zhì)上是指向二維數(shù)組的第i個(gè)元素,也就是第i個(gè)一維數(shù)組。對(duì)A+i解引用*(A+i)即得到一個(gè)數(shù)組A[i],*(A+i)就是這個(gè)數(shù)組的數(shù)組名(這樣說可能不合適,但是方便理解),同時(shí)依據(jù)數(shù)組名就是指向這個(gè)數(shù)組的首個(gè)元素的地址,可知*(A+i)實(shí)質(zhì)上還是一個(gè)指針,但是這個(gè)指針指向的是A[i][0]這個(gè)元素。
下面總結(jié)一下二維數(shù)組中的一些結(jié)論:
a :指向一維數(shù)組的指針。a -> a[0]
*a :得到一維數(shù)組a[0],可認(rèn)為(*a)是一維數(shù)組的數(shù)組名,而(*a)-> a[0][0]。
*a+j :指向a[0][j]的指針,(*a)可視為一個(gè)一維數(shù)組名。
a+i :數(shù)組a[i]的指針,也就是說a+i->a[i]。
*(a+i): 得到一維數(shù)組a[i],其中*(a+i)可以視為數(shù)組名。*(a+i)指向了a[i]的首個(gè)元素,即:*(a+i)->a[i][0]。
*(a+i)+j :得到數(shù)組*(a+i)的第j個(gè)元素的指針,即:*(a+i)+j -> a[i][j]。
*(*(a+i)+j) :就是得到a[i][j]。
我之前一直將二維數(shù)組看做指向指針的指針,現(xiàn)在想起來(lái)是不合適的,假設(shè)存在一個(gè)指向指針的指針int **p。如果另p = A,通常對(duì)p的操作都會(huì)導(dǎo)致一些錯(cuò)誤,由于p是一個(gè)指向指針的指針,那么p++實(shí)質(zhì)上只是增加了4個(gè)字節(jié),而A+1則增加了N*sizeof(int)。兩者之間并不是等價(jià)的關(guān)系,因此指向指針的指針并不能表征二維數(shù)組。
如果指向指針的指針a和二維數(shù)組名是等價(jià)的,那么下面的程序肯定能夠?qū)崿F(xiàn)數(shù)組元素的打印,但是非常不幸,下面的函數(shù)會(huì)產(chǎn)生段錯(cuò)誤即訪問了非法的內(nèi)存空間。
int print_array(int **a, int row, int col)
{
int i = 0 , j = 0;
for(i = 0; i < row; ++ i)
{
for(j = 0; j < col; ++ j)
printf("%d ",a[i][j]);
printf("");
}
}
簡(jiǎn)要的說明一下,為什么會(huì)產(chǎn)生段錯(cuò)誤吧,由于a是一個(gè)指向指針的指針,那么a[i]則是一個(gè)指針,他指向一個(gè)數(shù)據(jù)空間,由于數(shù)組名實(shí)際上對(duì)應(yīng)一個(gè)地址,這個(gè)地址和&A等都是相同的,a[i]實(shí)質(zhì)上是就是數(shù)組中的一個(gè)元素,但是其中的內(nèi)容也是一個(gè)指針,這時(shí)候再次解引用就很有可能導(dǎo)致內(nèi)存非法訪問,產(chǎn)生段錯(cuò)誤。比如A[2][3]={{1,2,3},{4,5,6}}。調(diào)用函數(shù)print_array(A,2,3)時(shí),由于A實(shí)質(zhì)上是一個(gè)地址,由于數(shù)組的特殊性,具有&A,A,A+0,&A[0][0]等對(duì)應(yīng)的值是相同的。a = A,實(shí)質(zhì)上a就是一個(gè)地址。這個(gè)地址就是數(shù)組的起始位置。a中的內(nèi)容實(shí)質(zhì)上就是數(shù)組的元素,因此對(duì)數(shù)組進(jìn)行一次解引用(*a)得到的實(shí)質(zhì)上就是數(shù)組的一個(gè)元素,也就是1,但是a是指向指針的指針,內(nèi)容1也是一個(gè)地址,對(duì)地址1的訪問肯定發(fā)生錯(cuò)誤,因?yàn)榈刂?處是屬于內(nèi)核,不能直接進(jìn)行訪問,發(fā)生了段錯(cuò)誤。這也就是為什么說二維數(shù)組和指向指針的指針之間并不是等價(jià)的。
但是根據(jù)上面的分析,我們知道二維數(shù)組的數(shù)組名指明了數(shù)組的位置,這時(shí)候直接對(duì)位置進(jìn)行訪問就能夠得到數(shù)組的元素。同時(shí)我們知道a++指向的地址恰好就是a+4。剛好也就是數(shù)組的下一個(gè)元素。因此我們可以采用解一次引用的方式實(shí)現(xiàn)數(shù)據(jù)的訪問。因此上面的代碼可以修改如下,這種方法是運(yùn)用了指針加法的一些特性,也就是指向指針的指針的增長(zhǎng)大小就是大小等于4,但是這種方法只能針對(duì)類型大小為4的數(shù)組問題,其他的類型不能運(yùn)用。
int print_array(int **a, int row, int col)
{
int i = 0 , j = 0;
for(i = 0; i < row; ++ i)
{
for(j = 0; j < col; ++ j)
printf("%d ",*(a+i+j));
printf("");
}
}
字符串?dāng)?shù)組是比較常用的,字符串?dāng)?shù)組定義如下:
char *str[]={
"One",
"Two",
"Three",
"Four"
};
字符串?dāng)?shù)組允許出現(xiàn)鋸齒形的數(shù)組,也就是說各個(gè)字符串的長(zhǎng)度可以是不相同的,我們知道字符串?dāng)?shù)組實(shí)質(zhì)上是一個(gè)指針數(shù)組,和二維數(shù)組之間存在一定的差別,并不是同一種類型的問題,指針數(shù)組可以轉(zhuǎn)換為指向指針的指針的問題。
我們常見的主函數(shù)main的兩種種定義方式為:
int main(int argc, char *argv[])
int main(int argc, char **argv)
這兩種定義方式使得很多的初學(xué)者認(rèn)為二維數(shù)組和指向指針的指針有一定的聯(lián)系,特別是第2種寫法存在很大的誤導(dǎo)性,實(shí)質(zhì)上字符串?dāng)?shù)組和二維數(shù)組之間存在很大的差別,所以不能當(dāng)做一類問題來(lái)討論。
二維數(shù)組作為函數(shù)的形參時(shí)也是一個(gè)應(yīng)該注意的問題。我們已經(jīng)知道二維數(shù)組不是指向指針的指針這個(gè)結(jié)論。那么如果需要傳遞一個(gè)二維數(shù)組時(shí)又該如何處理呢?我們知道數(shù)組名是一個(gè)指向數(shù)組的指針,將參數(shù)設(shè)置為這種形式即可:
int print_array(int (*p)[N],int row, int col);
int print_array(int a[M][N],int row, int col);
上面的兩種定義方式是比較常見的,當(dāng)然也可以采用指向指針的指針這種方式,這種方式一般在動(dòng)態(tài)分配二維數(shù)組的情況下使用。這時(shí)候的動(dòng)態(tài)分配的二維數(shù)組和我們當(dāng)前討論的二維數(shù)組存在差別,更像是字符串指針數(shù)組問題。
/*C++*/
void array_create(int **array, int row, int col)
{
int i = 0, j = 0;
array = new int *[row];
for(i = 0; i < row; ++ i)
{
array[i] = new int[col];
}
}
其中array首先分配行,然后行中保存了指針變量,這樣就實(shí)現(xiàn)了二維數(shù)組的動(dòng)態(tài)分配,這時(shí)候的二維數(shù)組不再是線性表,這是需要注意的。在C語(yǔ)言中也可以直接創(chuàng)建線性表,即采用一維數(shù)組訪問。
評(píng)論