程式碼
char *p = "asd";
printf(p); // 未加格式字串直接傳入指標
問題
`printf` 原型為 `int printf(const char *format, ...)`,參數要求 `const char*`。變數 `p` 為 `char*`(非 const),為何編譯通過且執行正常?
解析
- 字串常值儲存區:`"asd"` 是字串常值,存放於唯讀資料段(.rodata),屬於 `const char[4]` 型別。
- 指標指向常值:`char *p = "asd";` 將常值地址賦予非 const 指標 `p`。這在 C 標準中是允許但不建議的(C++11 起禁止隱性轉換),編譯器通常僅給警告。實際上 `p` 指向的記憶體不可修改,寫入會導致未定義行為(段錯誤)。
- 函數參數 const 資格:`printf(const char *format)` 的 `const` 意味著「函數內部不會透過此指標修改指向的內容」。呼叫端傳入 `char*` 會發生隱性合格轉換(qualification conversion):`char*` → `const char*`,這是安全的(增加限制),編譯器允許且不需顯式轉型。
- 執行行為:`printf` 將 `p` 視為格式字串。若 `p` 指向 `"asd"`(無 `%` 格式指示符),直接輸出 `asd`。若 `p` 指向 `"%s %d"` 且無後續參數,屬未定義行為。
正確寫法
const char *p = "asd"; // 正確:指標指向常值
printf("%s", p); // 安全:使用格式字串,明確意圖
// 或
puts(p); // 更簡潔,自動換行
核心觀念
- `const` 在參數表中表達「唯讀意圖」,呼叫端傳入非 const 指標會自動加上 const(安全)。
- 字串常值**永遠是唯讀的**,指標宣告應加 `const` 避免誤寫。
- 永遠使用格式字串(`printf("%s", p)`),切勿直接傳入使用者可控字串作為格式(格式字串漏洞)。