const 與指針的四種組合語義完整解析const 與指針結合產生四種基本聲明模式,搞清**「誰被修飾、誰只讀」**是寫出正確、可維護代碼的關鍵。本文從 int i 切入,層層遞進解析四種模式語義、代碼驗證與記憶口訣。
const 修飾變量 → 符號常量
const int ic = 20; // ic 成為只讀符號,不可修改
// ic = 40; // 編譯錯誤
const int 與 int const **完全等價** — const 修飾緊隨其后的類型關鍵字ic 不再是「變量」,而是「只讀符號常量」| 聲明 | 等價寫法 | const 修飾對象 | 指針值可改? | 指向值可改? | 俗稱 |
|---|---|---|---|---|---|
const int *p | int const *p | *p (指向的值) | ✅ 是 | ❌ 否 | 指向常量的指針 |
int *const p | 無 | p (指針本身) | ❌ 否 | ✅ 是 | 常量指針 |
const int *const p | int const *const p | *p 和 p 皆修飾 | ❌ 否 | ❌ 否 | 指向常量的常量指針 |
int *p | 無 | 無 | ✅ 是 | ✅ 是 | 普通指針 |
const int *p (或 int const *p) — 指向常量的指針
int i1 = 30, i2 = 40;
const int *p = &i1; // 初始化指向 i1
p = &i2; // ✅ 合法:指針值可改,重新指向 i2
// *p = 80; // ❌ 非法:不能通過 p 修改指向的值
i2 = 80; // ✅ 合法:直接修改 i2 本身 (i2 非 const)
printf("%d", *p); // 輸出 80
語義:p 是變量 (可指向別處),*p 是只讀視圖。
典型用途:函數參數 void func(const int *p) 承諾不修改調用者數據。
int *const p — 常量指針 (指針本身不可變)
int i1 = 30, i2 = 40;
int *const p = &i1; // 初始化時必須賦值,後不可改
// p = &i2; // ❌ 非法:指針值不可修改
*p = 80; // ✅ 合法:可通過 p 修改 i1
printf("%d", *p); // 輸出 80
語義:p 綁定初始地址終身不變,但可修改該地址存儲的值。
典型用途:硬件寄存器映射 volatile int *const REG = (int*)0x40001000; — 地址固定,值可讀寫。
const int *const p — 雙重只讀
int i = 30;
const int *const p = &i; // 初始化後 p 與 *p 皆不可改
// p = &i; // ❌
// *p = 40; // ❌
printf("%d", *p); // 唯一操作:只讀
語義:指針與指向值雙重保護,最高級只讀承諾。
const int 變量
const int ci = 40;
int *p;
// p = &ci; // ❌ 編譯錯:const 地址不能賦給非 const 指針 (丟棄 const 資格)
p = (int*)&ci; // ✅ 強制轉型編譯通過,但 *p = 80 運行時未定義行為 (可能崩潰/無效)
教訓:絕不 通過強制轉型移除 const 然後修改,違反只讀契約導致 UB。
const int *p 指向 const int 變量
const int ci = 40;
const int *p = &ci; // 類型完美匹配,雙重只讀保護
const 策略
// 1. 僅讀取數據,不修改 → const int * / const char *
void printArray(const int *arr, size_t n);
// 2. 輸出參數,需修改指向內容 → int * (無 const)
void readIntoArray(int *arr, size_t n);
// 3. 指針本身不應被修改 (罕見) → int *const
void legacyAPI(int *const handle); // handle 地址固定
「
const在*左邊 → 鎖定指向值 (*p 不可改);const在*右邊 → 鎖定指針本身 (p 不可改)。」
讀聲明時**從右向左**讀:int *const p → p is a const pointer to int;const int *p → p is a pointer to const int。
函數參數傳遞三大模式深度對決:**指針傳遞 vs 值傳遞 vs 引用傳遞 (C++)** — 為什麼以為 a,b 代替了 x,y 卻發現原值未變?揭開「傳遞假象」背後的棧幀真相。