初學者常困惑:同樣是 "test",為何 char *s = "test"; s[1] = 'E'; 崩潰,而 char s[] = "test"; s[1] = 'E'; 正常?本文從編譯器、鏈接器、運行時內存佈局三個層面徹底講透這一核心知識點。
// 情況 1:字符串字面量指針
char *s1 = "test";
s1[1] = 'E'; // 💥 段錯誤 / 訪問違例
// 情況 2:字符數組初始化
char s2[] = "test";
s2[1] = 'E'; // ✅ 正常,s2 變為 "tEst"
| 特性 | char *s = "test" | char s[] = "test" |
|---|---|---|
| 存儲區域 | 只讀數據段 (.rodata / .text) | 棧區 (函數棧幀) |
| 內存屬性 | 只讀 (Read-Only),硬件級保護 | 讀寫 (Read-Write) |
| 生命週期 | 程序全周期,啟動時加載 | 函數調用期間,棧幀銷毀時釋放 |
| 變量本質 | s 是指針 (8/4 字節),指向常量區 | s 是數組 (5 字節含 \0),數據在棧上 |
| sizeof 结果 | 指針大小 (8/4) | 數組長度 (5) |
char *a = "test"; char *b = "test"; 可能 a == b。"test" 字節逐個拷貝到棧上分配的數組空間,互不干擾。s1[1] 替換為常量 'e',但寫入操作仍觸發硬件異常。const char *s = "test"; 加 const 讓編譯器攔截寫入嘗試。char s[] = "test"; 或動態分配 char *s = strdup("test"); (記得 free)。const char *,內部需修改則拷貝副本。#include
int main() {
char *s1 = "test";
char s2[] = "test";
printf("s1 addr: %p (rodata)\n", (void*)s1);
printf("s2 addr: %p (stack)\n", (void*)s2);
printf("sizeof(s1)=%zu, sizeof(s2)=%zu\n", sizeof(s1), sizeof(s2));
// 驗證字面量合並
char *s3 = "test";
printf("s1==s3? %s\n", s1==s3 ? "yes (merged)" : "no");
return 0;
}
輸出將直觀展示地址差異、大小差異、字面量合並行為。