int a[3][4] = {{1,3,5,7},{9,11,13,15},{17,19,21,23}};
printf(FORMAT, a, *a);
printf(FORMAT, a[0], *(a+0));
printf(FORMAT, &a[0], &a[0][0]);
printf(FORMAT, a[1], a+1);
printf(FORMAT, &a[1][0], *(a+1)+0);
printf(FORMAT, a[2], *(a+2));
printf(FORMAT, &a[2], a+2);
printf(FORMAT, a[1][0], *(*(a+1)+0));
輸出結果「很奇怪,根本不是數組內的數」——**因為 %d 期望 int,實傳地址 (指針),解釋地址低 32 位為整數**。
內存佈局 (假設 int 4 字節,基址 0x1000): a[0][0]=1 a[0][1]=3 a[0][2]=5 a[0][3]=7 | a[1][0]=9 a[1][1]=11 ... | a[2][0]=17 ... 地址: 0x1000 0x1004 0x1008 0x100C | 0x1010 0x1014 | 0x1020 行跨度: 16 字節 (4*sizeof(int))
| 表達式 | 類型 | 值 (地址/內容) | 含義 |
|---|---|---|---|
a | int (*)[4] (指向含 4 個 int 的數組的指針) | 0x1000 | 整個二維數組首地址 (== &a == a[0]) |
*a / a[0] | int [4] (退化為 int*) | 0x1000 | 第 0 行首地址 |
&a[0] | int (*)[4] | 0x1000 | 第 0 行地址 (類型與 a 同) |
&a[0][0] | int* | 0x1000 | 第 0 行第 0 列元素地址 |
a[1] | int [4] → int* | 0x1010 | 第 1 行首地址 (基址 + 1行跨度) |
a+1 | int (*)[4] | 0x1010 | 指向第 1 行 (步進 1 行 = 16 字節) |
*(a+1) / a[1] | int [4] → int* | 0x1010 | 第 1 行首地址 |
&a[1][0] | int* | 0x1010 | 第 1 行第 0 列元素地址 |
*(a+1)+0 | int* | 0x1010 | 同 *(a+1) (指針算術 +0 無效) |
a[2] | int* | 0x1020 | 第 2 行首地址 |
*(a+2) | int* | 0x1020 | 第 2 行首地址 |
&a[2] | int (*)[4] | 0x1020 | 指向第 2 行 |
a+2 | int (*)[4] | 0x1020 | 指向第 2 行 |
a[1][0] | int | 9 | 第 1 行第 0 列**值** |
*(*(a+1)+0) | int | 9 | 解引用兩次得值 |
#define FORMAT "%d,%d\n" // 期望兩個 int
printf(FORMAT, a, *a); // 實傳:指針(0x1000), 指針(0x1000) -> 輸出地址低32位作為整數 (如 4194304, 4194304)
格式符與實參類型嚴重不匹配:%d 對應 int,傳入 int* 或 int(*)[4] 導致未定義行為,輸出地址數值而非數組內容。
// 方法1: 雙層循環打印每個元素
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", a[i][j]);
}
printf("\n");
}
// 方法2: 指針遍歷 (理解指針算術)
int *p = &a[0][0];
for (int i = 0; i < 12; i++) {
printf("%d ", p[i]); // 或 *p++
}
printf("\n");
// 方法3: 打印地址用 %p
printf("a=%p, *a=%p, a[0]=%p, &a[0]=%p\n", (void*)a, (void*)*a, (void*)a[0], (void*)&a[0]);
a (數組名) 在 **sizeof/&/_Alignof** 之外**退化為指向首元素的指針**。二維數組首元素是「第一行」,故退化為 int (*)[4]。a[i] 等價 *(a+i),類型為「行數組」int[4],隨即退化為 int* (行首地址)。&a[i] 類型 int (*)[4],值為第 i 行地址 (步進整行)。&a[i][j] 類型 int*,值為具體元素地址。a+i 步進 **整行** (× sizeof(int[4]) = 16 字節);*(a+i)+j 步進 **元素** (× sizeof(int) = 4 字節)。%p + (void*) 強轉,打印整數用 %d。二維數組指針表達式**類型決定步進粒度、解引用層級**。原代碼用 %d 打印指針地址導致「奇怪數字」。
**修正**:用雙層循環打印元素值,或用 %p 打印地址。徹底理解「數組退化規則、指針類型與算術、格式符匹配」三位一體,即可駕馭多維數組指針操作。