在大多數表達式中,陣列名 a 會隱性轉換為指向首元素的指標 &a[0] (型別 T*),這就是所謂的「退化」。但陣列名不是指標變數,關鍵差異在於:
| 特性 | 陣列 int a[10] | 指標 int *p |
|---|---|---|
| 儲存空間 | 編譯期配置 10*sizeof(int) 位元組 | 僅配置指標本身 (4/8 bytes) |
| 可修改性 | 常量,不可賦值 a = p;、a++ | 變數,可賦值、算術、遞增 |
sizeof | 整個陣列大小 (如 40 bytes) | 指標大小 (4/8 bytes) |
&a 型別 | int (*)[10] (指向陣列的指標) | int ** (指向指標的指標) |
| 函數參數 | 退化為 int*,sizeof 失效 | 原本就是 int* |
設 int a[10]; int *p = a; (p 指向 a[0]):
| 陣列寫法 | 指標寫法 | 含義 |
|---|---|---|
a[i] | *(p + i) | 第 i 元素值 |
&a[i] | p + i | 第 i 元素位址 |
a + i | p + i | 指向第 i 元素的指標 |
指標亦可用下標:p[i] 等價 *(p+i),編譯器產生相同機器碼。
char s[30], *p;
p = s; /* p 指向 s[0] */
p[8] = 'X'; /* 修改 s[8] */
*(p + 8) = 'Y'; /* 同樣修改 s[8] */
p += 5; /* p 指向 s[5] (指標可算術) */
/* s += 5; 錯誤:陣列名不可修改 */
char s[30]; 編譯器自動配置/釋放,大小固定。char *p = malloc(30); 執行期決定大小,需手動 free。p[i], *(p+i))。
int a[3][4]; /* 3 列、4 行 */
int (*p)[4] = a; /* p 指向「含 4 個 int 的陣列」 */
/* 正確用法 */
p[1][2] = 99; /* 等價 a[1][2] = 99 */
*(p[1] + 2) = 99; /* 同 */
*(*(p + 1) + 2) = 99; /* 同 */
/* 錯誤 */
int *q = a; /* 型別不合:a 退化為 int(*)[4],非 int* */
int **r = a; /* 更錯 */
void f1(int a[10]) { /* 實為 void f1(int *a) */ }
void f2(int a[][4]) { /* 保留行大小,實為 void f2(int (*a)[4]) */ }
void f3(int (*a)[4]) { /* 明確指標陣列型別 */ }
/* 呼叫端 */
int m[3][4];
f1(m); /* 警告:int(*)[4] → int* 丟失行資訊 */
f2(m); /* 正確 */
f3(m); /* 正確 */
void f(int *a, size_t n) 明確表達「指標+長度」契約。void f(int (*a)[4], int rows)。-Warray-bounds、-Wpointer-arith 靜態分析。