首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C >> C 語言問答

C語言二維數組指針陷阱:a、&a、a[0]、*a、a+1 類型與值完整圖解

     編輯:C 語言問答

問題代碼現象


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 基址 0x1000)

表達式類型值 (地址/內容)含義
aint (*)[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+1int (*)[4]0x1010指向第 1 行 (步進 1 行 = 16 字節)
*(a+1) / a[1]int [4] → int*0x1010第 1 行首地址
&a[1][0]int*0x1010第 1 行第 0 列元素地址
*(a+1)+0int*0x1010同 *(a+1) (指針算術 +0 無效)
a[2]int*0x1020第 2 行首地址
*(a+2)int*0x1020第 2 行首地址
&a[2]int (*)[4]0x1020指向第 2 行
a+2int (*)[4]0x1020指向第 2 行
a[1][0]int9第 1 行第 0 列**值**
*(*(a+1)+0)int9解引用兩次得值

為什麼原代碼輸出「奇怪數字」?


#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]);

關鍵規則速記

  1. a (數組名) 在 **sizeof/&/_Alignof** 之外**退化為指向首元素的指針**。二維數組首元素是「第一行」,故退化為 int (*)[4]。
  2. a[i] 等價 *(a+i),類型為「行數組」int[4],隨即退化為 int* (行首地址)。
  3. &a[i] 類型 int (*)[4],值為第 i 行地址 (步進整行)。
  4. &a[i][j] 類型 int*,值為具體元素地址。
  5. a+i 步進 **整行** (× sizeof(int[4]) = 16 字節);*(a+i)+j 步進 **元素** (× sizeof(int) = 4 字節)。
  6. 打印指針必用 %p + (void*) 強轉,打印整數用 %d。

總結

二維數組指針表達式**類型決定步進粒度、解引用層級**。原代碼用 %d 打印指針地址導致「奇怪數字」。
**修正**:用雙層循環打印元素值,或用 %p 打印地址。徹底理解「數組退化規則、指針類型與算術、格式符匹配」三位一體,即可駕馭多維數組指針操作。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved