核心等價關係
int a[5] = {10, 20, 30, 40, 50};
// 以下四種寫法完全等價,生成相同機器碼:
a[i] // 下標語法糖
*(a + i) // 指標算術:a 退化為指向首元素指標,+i 位移 i 個 int
*(i + a) // 加法交換律,同樣合法
i[a] // 同樣合法!*(i+a) → a[i],C 允許此寫法 (但請勿實際使用)
標準定義 (C99 6.5.2.1)
a[b] **定義為** *(a + b)。其中 a 為指標表達式,b 為整數表達式。
陣列名 a 的雙重身分
| 場合 | 表現 | 型別 |
| **sizeof(a)** | 整個陣列大小 (如 5*4=20 位元組) | int [5] |
| **&a** | 陣列首地址,**型別為指向整個陣列的指標** | int (*)[5] |
| **其他場合** (傳參、賦值、運算) | **退化** 為指向首元素的指標 | int * |
退化後失去長度資訊
void func(int arr[]) { // 等價 int *arr
printf("%zu\n", sizeof(arr)); // 輸出 8 (64位指標大小),非陣列大小
}
int a[100];
func(a); // a 退化為 int*,長度 100 丟失
**對策**:函數參數同時傳遞長度 void func(int *arr, size_t n),或用結構體封裝 struct { int *data; size_t len; }。
指標 vs 陣列:本質差異
| 特性 | 陣列 int a[10] | 指標 int *p |
| 記憶體分配 | 編譯期靜態/棧上連續 10*sizeof(int) | 僅儲存位址 (8 位元組),指向資料需另行分配 |
| 可修改指向 | 不可 (a 是常量指標,非左值) | 可 (p = malloc(...); p++) |
| sizeof | 整個陣列大小 | 指標大小 (4/8 位元組) |
| 初始化 | int a[] = {1,2,3}; | int *p = malloc(...); 或指向現有物件 |
| 字串字面量 | char s[] = "abc"; 可修改 | char *p = "abc"; 指向唯讀區,**不可修改** |
指標算術與下標互換實例
// 字串長度 (不呼叫 strlen)
size_t my_strlen(const char *s) {
const char *p = s;
while (*p) p++;
return p - s; // 指標相減得元素個數
}
// 反轉字串 (雙指標)
void reverse(char *s) {
char *left = s, *right = s;
while (*right) right++; // 移至 \0
right--; // 最後一個字元
while (left < right) {
char tmp = *left;
*left++ = *right;
*right-- = tmp;
}
}
避坑指南
int *p = a; 後 p++ 合法,a++ **非法** (陣列名非左值)。
- 函數參數
int a[]、**永遠視為 int *a**,sizeof(a) 在函數內**不可用**取陣列長度。
char *p = "literal"; → p[0] = 'x'; **未定義行為** (寫入唯讀記憶體)。正確:char s[] = "literal";。
- 動態陣列:
int *arr = malloc(n * sizeof(int)); 用完 free(arr);,arr = NULL;。