C 語言二維數組本質是「數組的數組」,指針運算極易出錯。本文從 *a 引用語義出發,層層推導二維數組指針表達式、參數傳遞、指向數組的指針聲明,並展示遍歷陷阱與編譯器兼容性問題。
*a ≡ a[0] // 首元素引用
*(a+i) ≡ a[i] // 第 i 元素引用
a+i ≡ &a[i] // 第 i 元素地址
甚至 i[a] 合法(因 a+i ≡ i+a),但**強烈不推薦**。
int calendar[12][31]; // 12 個元素的數組,每元素是 31 個 int 的數組
calendar[4] 是什麼?int[31](含 31 個 int 的數組)sizeof(calendar[4]) == 31 * sizeof(int)int* 指向首元素
calendar[4][7]
≡ *(calendar[4] + 7)
≡ *(*(calendar + 4) + 7)
int *p;
p = calendar; // ❌ 錯誤:類型不匹配
// calendar 是 int(*)[31](指向數組的指針)
// p 是 int*(指向 int 的指針)
int (*ap)[31]; // ap 是指向 int[31] 的指針
int (*monthp)[31] = calendar; // ✅ 合法
讀法:*ap 是數組 → ap 是指向數組的指針。
for(month=0; month<12; month++)
for(day=0; day<31; day++)
calendar[month][day] = 0;
int (*monthp)[31];
for(monthp = calendar; monthp < &calendar[12]; monthp++) {
int *dayp;
for(dayp = *monthp; dayp < &(*monthp)[31]; dayp++)
*dayp = 0;
}
⚠️ 早期編譯器可能不支援 &(*monthp)[31] 寫法,現代 GCC/Clang/MSVC 均可編譯。
| 聲明 | 實際類型 | 適用場景 |
|---|---|---|
void f(int a[12][31]) | int (*a)[31] | 列數固定 |
void f(int a[][31]) | int (*a)[31] | 列數固定,行數不固定 |
void f(int *a[12]) | int **a | 指針陣列(鋸齒) |
void f(int **a) | int **a | 指針陣列 |
// 二維數組:連續塊 int a[3][4]; // sizeof=48, a+1 增加 16 bytes // 指向數組的指針 int (*p)[4] = a; // p+1 增加 16 bytes // 指針陣列:分散塊 int *q[3]; // q+1 增加 8 bytes (64位)
int (*p)[N] 指向二維數組malloc(rows * cols * sizeof(int)) + 手動索引int ** 接收二維數組
// 方法 1:單塊連續(推薦,緩存友好)
int *flat = malloc(rows * cols * sizeof(int));
#define IDX(r,c) flat[(r)*cols + (c)]
// 方法 2:指針陣列(行長度可變)
int **rows = malloc(rows * sizeof(int*));
for(i=0; i