| 特性 | char *p = "123"; | char p[] = "123"; |
|---|---|---|
| 存儲位置 | 字符串常量在 .rodata 段 (唯讀),指針 p 在棧/數據段 | 字符數組在 棧 (局部) 或 .data/.bss (全局),內容可寫 |
| 可修改性 | 不可修改內容 (*p = 'x' → 段錯誤),指針可改 | 可修改內容 (p[0] = 'x' 合法) |
sizeof | 指針大小 (4/8 字節) | 數組長度含結束符 4 (含 \0) |
| 賦值語義 | 指針指向常量區地址 | 編譯器生成代碼逐字符拷貝 (strcpy 效果) |
| 類型 | char* (實為 const char*) | char[4] (退化為 char* 時失去長度信息) |
// char *p = "123"; 棧/數據段: p ──────► .rodata 段: '1' '2' '3' '\0' (唯讀) // char p[] = "123"; 棧/數據段: p[0]='1', p[1]='2', p[2]='3', p[3]='\0' (可讀寫)
#include
#include
int main() {
char *p1 = "abcdef";
char p2[] = "abcdef";
printf("sizeof(p1)=%zu sizeof(p2)=%zu\n", sizeof(p1), sizeof(p2));
// 64位輸出: sizeof(p1)=8 sizeof(p2)=7
// p1[0] = 'g'; // 運行時崩潰:Segmentation fault (寫唯讀段)
++p1; // 合法:修改指針本身
printf("p1 now: %s\n", p1); // "bcdef"
p2[0] = 'g'; // 合法:修改數組內容
printf("p2 now: %s\n", p2); // "gbcdef"
return 0;
}
void Func1(char *str) { str[0] = 'X'; } // 若傳字符串常量 → 崩潰
void Func2(char str[]) { str[0] = 'X'; } // 同義,退化為指針
// 安全調用
char buf[] = "hello";
Func1(buf); // OK
Func1("hello"); // 崩潰!
// 推薦簽名:明確只讀意圖
void SafeFunc(const char *str); // 編譯器會檢查傳入字符串常量合法,傳數組也合法
// 指針數組:每個元素指向字符串常量
char *arr1[] = {"one", "two", "three"}; // arr1 在棧,指向 .rodata
// 二維數組:連續存儲,可修改
char arr2[][10] = {"one", "two", "three"}; // 每行 10 字節,含 \0
// sizeof 對比
sizeof(arr1); // 3 * sizeof(char*) = 24 (64位)
sizeof(arr2); // 3 * 10 = 30
const char* p = "..." 或 const char p[] = "...",編譯器強制保護。char buf[SIZE] 或動態分配 malloc/new。const char*,輸出緩衝區用 char* buf, size_t len。"abc" == "abc" 可能為真),切勿依賴地址唯一性。std::string/std::string_view:自動管理內存、避免指針語義陷阱。char *p = "..." 是指向唯讀內存的指針,char p[] = "..." 是可修改的字符數組副本。搞清存儲段、可修改性、sizeof 語義、函數參數退化,是避免段錯誤、邏輯 Bug、安全漏洞的關鍵。