int a[10];
a[i] // 由編譯器轉換為 *(a + i)
*(a+i) // 指針算術:地址 + i * sizeof(int)
i[a] // 合法但極不推薦(加法交換律)
本質:數組名在表達式中(除 sizeof、& 外)退化為指向首元素的指針。
int calendar[12][31]; // 12 個「含 31 個 int 的數組」組成的數組
calendar:指向首個「31-int 數組」的指針,類型 int (*)[31]。calendar[4]:第 5 個子數組,類型 int [31],退化為 int * 指向其首元素。calendar[4][7] ≡ *(calendar[4] + 7) ≡ *(*(calendar + 4) + 7)。int (*monthp)[31] = calendar; // 指向「31-int 數組」的指針
for (monthp = calendar; monthp < &calendar[12]; monthp++) {
int *dayp = *monthp; // *monthp 退化為 int*,指向該行首元素
for (dayp = *monthp; dayp < &(*monthp)[31]; dayp++) {
*dayp = 0;
}
}
關鍵點:
monthp++ 步進 sizeof(int[31]) (124 字節),完美跨行。&calendar[12] 是「第 13 行」地址(不存在),作為終止哨兵,合法參與比較。&(*monthp)[31] 同理為行尾哨兵。int **p = calendar; 為何非法?calendar 類型 int (*)[31](指向數組的指針)。int **p 類型 int **(指向指針的指針)。char *strs[] ≡ char **strs因為字符串處理只關心 char * 層面,printf("%s", strs[i]) 只需一級解引用,二級指針與數組退化指針在這種用法下行為一致。
| 場景 | 推薦 | 理由 |
|---|---|---|
| 一般業務代碼 | 下標 a[i][j] | 可讀性強,編譯器優化同等 |
| 高性能掃描/拷貝 | 指針 *p++ | 減少索引計算,利用自增寻址模式 |
| 動態分配二維數組 | 指針數組 int **p + 每行 malloc | 行長可變,非連續內存 |
| 靜態大小、需連續內存 | 真二維數組 int a[M][N] | 單塊內存,緩存友好,指針步進正確 |