指標的 const 正確性
對一個普通指標,你可能想鎖住兩種不同的東西:它所指的值,以及它指向哪裡。const 關鍵字讓你能禁止其中任一者被改動——而 const 擺放的位置決定鎖的是哪一個。把這件事弄對,就是大家說的 const 正確性,重點多半在於仔細閱讀宣告。
從變數名稱往外讀。指向 const 的指標,寫成 const int *p,意思是「p 可以移動,但你不能透過它寫入」——*p = 5 會被拒絕,而 p = &other 沒問題;透過這個指標看,被指物是唯讀的。const 指標,寫成 int *const p,則相反:「p 固定不動,但它所指名的值可以變」——p = &other 會被拒絕,而 *p = 5 沒問題。至於 const int *const p 則兩者皆鎖。一個好用的口訣:const 緊貼在誰的左邊,誰就不能變——星號左邊的 const 保護被指物,星號右邊的 const 保護指標。指向 const 的指標是日常主力:承諾不修改輸入的函式會接收 const char *,例如 strlen(const char *s)。
const 正確性不是由硬體強制的執行期保證;它是編譯器會檢查的編譯期承諾,也是型別裡的一段文件。它能攔住不小心的寫入、讓呼叫者安全地傳遞唯讀資料、並傳達意圖。誠實的提醒:你可以把 const 轉掉(cast away),而透過這樣取得的指標去寫一個本來真的是 const 的物件,是未定義行為——const 是你該遵守的契約,不是撬不開的鎖。
const int *a; 允許 a = &y 但拒絕 *a = 1(被指物唯讀)。int *const b = &x; 允許 *b = 1 但拒絕 b = &y(指標固定)。由右往左讀:「a 是指向 const int 的指標」;「b 是指向 int 的 const 指標」。
星號左邊鎖被指物;星號右邊鎖指標。
指標上的 const 是在編譯期檢查,不是由記憶體保護強制的——而且它可以被轉掉。這樣做去寫一個真正 const 的物件是未定義行為。把 const 當成關於意圖的誠實承諾,對於你不修改的輸入優先採用 const char *。