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