int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} };
從概念上理解,a 的分布像一個矩陣:0 1 2 3 4 5 6 7 8 9 10 11但在內存中,a 的分布是一維線性的,整個數組占用一塊連續的內存:
int (*p)[4] = a;
括號中的*
表明 p 是一個指針,它指向一個數組,數組的類型為int [4]
,這正是 a 所包含的每個一維數組的類型。[ ]
的優先級高於*
,( )
是必須要加的,如果赤裸裸地寫作int *p[4]
,那麼應該理解為int *(p[4])
,p 就成了一個指針數組,而不是二維數組指針,這在《C語言指針數組》中已經講到。int [4]
,那麼p+1
就前進 4×4 = 16 個字節,p-1
就後退 16 個字節,這正好是數組 a 所包含的每個一維數組的長度。也就是說,p+1
會使得指針指向二維數組的下一行,p-1
會使得指針指向數組的上一行。p
指向數組 a 的開頭,也即第 0 行;p+1
前進一行,指向第 1 行。*(p+1)
表示取地址上的數據,也就是整個第 1 行數據。注意是一行數據,是多個數據,不是第 1 行中的第 0 個元素,下面的運行結果有力地證明了這一點:
#include <stdio.h> int main(){ int a[3][4] = { {0, 1, 2, 3}, {4, 5, 6, 7}, {8, 9, 10, 11} }; int (*p)[4] = a; printf("%d\n", sizeof(*(p+1))); return 0; }運行結果:
*(p+1)+1
表示第 1 行第 1 個元素的地址。如何理解呢?*(p+1)
單獨使用時表示的是第 1 行數據,放在表達式中會被轉換為第 1 行數據的首地址,也就是第 1 行第 0 個元素的地址,因為使用整行數據沒有實際的含義,編譯器遇到這種情況都會轉換為指向該行第 0 個元素的指針;就像一維數組的名字,在定義時或者和 sizeof、& 一起使用時才表示整個數組,出現在表達式中就會被轉換為指向數組第 0 個元素的指針。*(*(p+1)+1)
表示第 1 行第 1 個元素的值。很明顯,增加一個 * 表示取地址上的數據。
a+i == p+i
a[i] == p[i] == *(a+i) == *(p+i)
a[i][j] == p[i][j] == *(a[i]+j) == *(p[i]+j) == *(*(a+i)+j) == *(*(p+i)+j)
#include <stdio.h> int main(){ int a[3][4]={0,1,2,3,4,5,6,7,8,9,10,11}; int(*p)[4]; int i,j; p=a; for(i=0; i<3; i++){ for(j=0; j<4; j++) printf("%2d ",*(*(p+i)+j)); printf("\n"); } return 0; }運行結果:
0 1 2 3 4 5 6 7 8 9 10 11
int *(p1[5]); //指針數組,可以去掉括號直接寫作 int *p1[5]; int (*p2)[5]; //二維數組指針,不能去掉括號指針數組和二維數組指針有著本質上的區別:指針數組是一個數組,只是每個元素保存的都是指針,以上面的 p1 為例,在32位環境下它占用 4×5 = 20 個字節的內存。二維數組指針是一個指針,它指向一個二維數組,以上面的 p2 為例,它占用 4 個字節的內存。