在 C 語言中,const 關鍵字的位置決定了它修飾的是「指針本身」還是「指針指向的值」。這是許多開發者容易混淆的關鍵點,搞清楚它能避免大量難以察覺的 Bug。
const int *p(或 int const *p)—— 常量指針const 修飾 *p,即指針指向的值不可修改。p 前無 const,指針變量可重新指向其他地址。int const *p 與 const int *p 完全相同。int *const p —— 指針常量const 修飾 p,即指針本身的值(地址)不可修改,必須在定義時初始化。*p 前無 const,可通過解引用修改指向內存的值。const int *p): 鎖定的是 *p(指向的值),p 可變 → 常量指針。int *const p): 鎖定的是 p(指針本身),*p 可變 → 指針常量。const int *const p雙重 const:指針不可變,指向的值也不可變。常用於函數參數傳遞只讀數據且防止指針被竄改。
#include <stdio.h>
int main(void) {
int i = 30, j = 40;
int c = 10, d = 20;
// 測試 const int *p (常量指針)
const int *p1 = &i;
p1 = &j; // ✅ 合法:修改指向
// *p1 = 80; // ❌ 錯誤:嘗試修改只讀值
// 測試 int *const p2 (指針常量)
int *const p2 = &i;
*p2 = 80; // ✅ 合法:修改指向的值
// p2 = &j; // ❌ 錯誤:嘗試修改指針地址
// 測試 const int *const p3 (雙重 const)
const int *const p3 = &c;
// *p3 = 80; // ❌ 錯誤
// p3 = &d; // ❌ 錯誤
// 經典陷阱:指向常量的指針 vs 非常量指針
const int a = 40;
// int *p4 = &a; // ❌ 編譯錯誤:不能用非常量指針指向常量
int *p4 = (int *)&a; // ✅ 強制轉型(極度危險,僅作演示)
*p4 = 80;
printf("a = %d\n", a); // 輸出 40(常量區可能未真正修改)
printf("*p4 = %d\n", *p4); // 輸出 80(未定義行為)
return 0;
}
| 聲明 | 俗稱 | 指針可變? | 指向值可變? | 典型用途 |
|---|---|---|---|---|
const int *p | 常量指針 | 是 | 否 | 函數參數傳遞只讀數組/緩衝區 |
int *const p | 指針常量 | 否 | 是 | 固定硬體寄存器地址、單例模式 |
const int *const p | 常量指針常量 | 否 | 否 | 嚴格只讀、防篡改場景 |
核心原則: 讀聲明時從右向左讀(p is a const pointer to int),或遵循「const 綁定最近的標識符」規則,即可秒懂所有組合。