本文屬於第三階段產物,通過標準引用、反彙編、運行時驗證,釐清字符串常量的存儲真相。
語法:雙引號括起的多字節字符序列,如
"xyz"。
類型:char[](非const char[],歷史兼容 K&R C)。
終止符:編譯階段 7 自動追加\0。
修改行為:未定義行為 (UB)。
合併優化:相同字面量是否合併為單一實例,屬於實現定義。
char[] 而非 const char[]?K&R C 無 const 關鍵字,ANSI C 引入 const 時為保持向後兼容,未修改字面量類型。但標準明確規定:修改字面量導致 UB。
static char* sayHi = "Hi Fene!";
static char* sayHello = "Hello Fene!";
static char* dummyHi = "Hi Fene!"; // 與 sayHi 內容相同
void print() {
printf("sayHi:%s, sayHello:%s, dummyHi:%s\n", sayHi, sayHello, dummyHi);
}
gcc -m32 -Wall main.c -o main
objdump -s main
Contents of section .rodata: 80484f8 48692046 656e6521 00 Hi Fene!. 8048501 48656c6c 6f204665 6e652100 Hello Fene!.. 804850e 48692046 656e6521 00 Hi Fene!.
關鍵發現:
sayHi 和 dummyHi 指向不同地址(各自有一份 "Hi Fene!" 副本),未合併。00 (\0)。| 平台 | 相同字面量是否合併 | 存儲段 |
|---|---|---|
| Linux x86 (GCC) | 否 | .rodata |
| Linux x64 (GCC) | 否 | .rodata |
| Linux ARM (GCC) | 否 | .rodata |
| Windows MSVC | 是 (/GF 默認開啟) | .rdata |
結論:不可假設相同字面量共享地址,比較字符串內容必須用 strcmp,而非指針比較。
char *p = "i'm hungry!";
p[0] = 'I'; // UB:段錯誤
char a[3] = "abc"; // 合法,但無 \0
printf("%s", a); // UB:讀越界
strcpy(b, a); // UB
while(1) print("Hello"); // 若 print 內部修改,下次循環看到的不是 "Hello"
const char*。char buf[] = "hello";(存棧上,可寫)。char[],存儲於 .rodata 唯讀段,帶隱式 \0。