1. 程式人生 > >為什麼不能用二級指標直接指向二維陣列

為什麼不能用二級指標直接指向二維陣列

先上程式碼:

int a[2][3]={1,2,3,4,5,6};//2行3列的int型陣列

int **pp=a;//編譯出錯,不能用二級指標直接指向二維陣列

int (*p)[3]=a;//對,p是指向一維陣列的指標,可以指向二維陣列

int *p1=a[0];//可以,p1也是一維指標,可以指向二維陣列

可以將上述的程式碼自己上機試試。

按理說一維陣列對應一維指標,即類似於int a[3]; int *p=a;
二維陣列應該也對應於二級指標才對啊,但是其實不然。

要想解釋清楚,先想想二級指標的定義,

定義:

  • 二級指標是指向一級指標的指標,一級指標是取一次地址,二級指標是取兩次地址。
    這個沒有異議吧。 這個可以擴充套件成:n級指標是指向n-1級指標的指標,n級指標是取n次地址。

要回答題目的答案,我覺得可以有兩個解釋。

解釋a:不需要用二級指標

我們想想,對二維陣列應該取多少次地址呢,答案是一次
為什麼呢,有人會問二維陣列不是對應著兩次嗎?
給出原因:
因為二維陣列是連續存放的。取了一次地址之後,所有元素的地址就全部知道了,只需要指標的移動就可以遍歷所有的元素了,不需要再取一次地址了。

像上面程式碼中的二維陣列a,你只要知道了a,就可以知道a[0]的地址,(因為a=&a[0]),通過指標的移動也就是知道a[i][j]的地址了。
舉個栗子,也就是上面的指向一維陣列的指標p,

int a[2][3]={1,2,3,4,5,6};
int (*p)[3]=a;
//p指向了a,就是指向了a[0]為陣列名的一維陣列,陣列元素
//為1、2、3,p的移動,實現了a[0]、a[1]、a[2]這三個一維陣列之間移動。
for(int i=0;i<2;i++)
    for(int j=0;j<3;j++)
    {
        printf("%d ",p[i][j]);//可以看到,一級指標p可以實現對二維陣列的遍歷
    }

栗子2:二維陣列實際上可以看出兩個一維數組合併成一個一維陣列,
所以像一開始的指標p1,int *p1=a[0];
指標p1=a[0]就是p1=&a[0][0],因為這六個元素都是連續存放的,所以p1的移動也就是實現了對這個長度為6的“一維陣列”,即對二維陣列a[2][3]的遍歷。

解釋b:不可以


解釋a說明了不需要用二級指標來指向二維陣列,那是不是說明需要的時候是可以這麼用的?不是。
根本就不可以這麼用。
先說說上面的程式碼二級指標pp的出錯的問題:
int **pp=a;//編譯出錯,不能用二級指標直接指向二維陣列
之所以會編譯出錯,是因為
int **pp=a;//
//等價於
int **pp=&a[0];//因為a=&a[0],所以變數pp裡面放著的內容就是a[0]的地址
//又進一步等價於
int **pp=&a[0][0];//因為&a[0][0]的值和&a[0]的值一樣,都是數字1的地址


//所以最終推得 pp裝的就是a[0][0]的地址。

所以pp=&a[0][0],即*pp=1;

那麼你想想還可以對*pp再取一次地址嗎,現在*pp裡面放的是數字1,再加一個*號的話,**pp訪問的就是實體地址為1的內容了。這是不允許的。所以會出錯。
這裡所用的知識也還是解釋a裡面的東西。

還有一點,二級指標是指向一級指標的,那麼二級指標pp每次移動的大小就是sizeof(int *)也就是4個位元組,所以pp+1不是像二維陣列a+1那樣移動到下一行首地址,而是移動四個位元組。關於指標移動的差別也進一步說明了不可以用二級指標指向二維陣列。

那有人會提問了,int **pp=a;不行,那int **pp=&a;總可以了吧。
答案還是不行
原因也還是上文講的移動的大小不一致導致的。

下面說說什麼時候可以用

char *str[2]={"hello","world"};
char **pp;
pp=str;//這種情況是可以的。

本質原因是str[2]為一維陣列,但是它的兩個元素都是char*型的,都是地址。所以,*pp=str[0],str[0]不是像上面的那樣是個定值1,這裡的str[0]是一個地址,是字串“hello”的首地址,所以還要再取一次地址才能訪問到字串“hello”的每個字元。這就是需要兩次取地址了。這時候用二級指標就沒有問題了。二級指標不就是取兩次地址嘛。
況且這裡指標的移動也沒有問題,char*本來就是四個位元組的。