const 與指標結合時,位置不同語義截然不同。掌握 「誰被 const 修飾,誰就不可變」** 核心原則,能徹底搞懂四種組合。本文透過可編譯驗證的程式碼,逐一解析。
const int *p → p 是指標 → 指向 const int → 指向的值不可變int const *p → 同上 (同義)int *const p → p 是 const 指標 → 指標本身不可變const int *const p → 兩者皆不可變int *p
int i = 1;
int *p = &i;
(*p)++; // ✓ 修改值
p = &j; // ✓ 修改指向
const int *p / int const *p
const int *p = &i; // 或 int const *p
i = 2; // ✓ 透過原變數修改值
p = &j; // ✓ 修改指向
(*p)++; // ✗ 編譯錯誤:assignment of read-only location
語義:p 指向的資料唯讀,但 p 可指向其他位址。
int *const p
int *const p = &i; // 初始化後不可改指向
(*p)++; // ✓ 修改值
p = &j; // ✗ 編譯錯誤:assignment of read-only variable 'p'
語義:p 本身是常數 (必須初始化),但可透過 p 修改指向的資料。
const int *const p
const int *const p = &i;
i = 2; // ✓ 原變數可改
(*p)++; // ✗ 唯讀
p = &j; // ✗ 指標本身唯讀
語義:雙重保護,指標與指向資料皆不可變。
| 宣告 | 指標可改? | 值可改? | 常見用途 |
|---|---|---|---|
int *p | ✓ | ✓ | 一般修改資料 |
const int *p | ✓ | ✗ | 函式參數防誤改、唯讀檢視 |
int *const p | ✗ | ✓ | 固定硬體暫存器位址、單一物件綁定 |
const int *const p | ✗ | ✗ | 常數表格、查找表、嵌入式常數位址 |
// 1. 承諾不修改字串內容
int strlen(const char *str);
// 2. 固定緩衝區位址,只填充資料
void read_sensor(int *const buffer, int len);
// 3. 完全不可變的查找表
const int *const kPidGains = pid_table;
| 錯誤程式碼 | 問題 | 修正 |
|---|---|---|
const int *p = &i; *p = 10; | 嘗試修改唯讀位置 | 移除 const 或改用非 const 指標 |
int *const p; p = &i; | 常數指標未初始化 | 宣告時必須初始化 int *const p = &i; |
void f(const int *p) { int *q = p; } | 隱性移除 const (編譯器警告) | 接收端也加 const const int *q = p; |
專家建議:預設所有指標參數加
const,除非函式確實需要修改指向資料。這能讓編譯器幫你抓出大量潛在 Bug,也是現代 C/C++ 核心指針 (C++ Core Guidelines) 強力推薦的做法。