| 概念 | 關鍵字/語法 | 類型 | 儲存位置 | 可修改性 | 典型用途 |
|---|---|---|---|---|---|
| 字元常量 | 'A' | int | 立即值/常數區 | 不可修改 (右值) | 字元比較、switch case、ASCII 運算 |
| 字元變數 | char c | char (1 byte) | 棧 (區域) / 數據段 (全域) | 可修改 | 儲存單一字元 |
| 字串字面值 | "Hello" | const char[] → 退化為 const char* | 唯讀數據段 (.rodata) | 不可修改 (UB) | printf 格式字串、初始化指標/陣列 |
| 字元陣列 | char str[] = "Hello" | char[6] | 棧 (區域) / 數據段 (全域) | 可修改 | 可讀寫字串緩衝區、函數參數 |
| 字元指標 | char *p = "Hello" | char* (指向唯讀區) | 指標在棧,數據在唯讀區 | 指標可改指向,**數據不可改** | 遍歷唯讀字串、函數參數 const char* |
| 動態字串 | char *p = malloc(n) | char* | 堆 | 可修改、可擴容 | 長度未知/極長字串 |
char str[] vs char *str
char str1[] = "Hello"; // 陣列:編譯器分配 6 字節棧空間,複製 "Hello\0" 進去
char *str2 = "Hello"; // 指標:指向唯讀區 "Hello\0",僅複製指標值 (地址)
str1[0] = 'h'; // ✅ 合法,修改棧上副本
str2[0] = 'h'; // ❌ 未定義行為 (段錯誤)!嘗試寫入唯讀記憶體
sizeof(str1) == 6; // 陣列大小 (含 \0)
sizeof(str2) == 8; // 指標大小 (64位系統),非字串長度
<string.h>)strlen(s):長度 (不含 \0)。strcpy(dest, src) / strncpy:複製 (需確保 dest 空間足夠)。strcat(dest, src) / strncat:拼接。strcmp(s1, s2):比較 (返回 <0, 0, >0)。strchr(s, c) / strrchr:查找字符。strstr(s1, s2):查找子串。sprintf(buf, fmt, ...) / snprintf:格式化輸出到緩衝區 (防溢出用 snprintf)。const char*:void print(const char *s) 承諾不修改,可接受字面值、陣列、動態記憶體。void get_str(char *buf, size_t sz),避免函數內 malloc 導致記憶體洩漏或所有權不清。\0 至關重要:手動構造字串時必須手動添加;strncpy 不自動補 \0 (若 src 長度 ≥ n)。gets, strcat, sprintf 無長度限制版本:改用 fgets, strncat, snprintf。
字串字面值 "Hello" (唯讀段 .rodata):
+------+------+------+------+------+------+
| 'H' | 'e' | 'l' | 'l' | 'o' | '\0' | 地址: 0x400000
+------+------+------+------+------+------+
char str1[] = "Hello" (棧):
+------+------+------+------+------+------+
| 'H' | 'e' | 'l' | 'l' | 'o' | '\0' | 地址: 0x7FFEE... (棧地址)
+------+------+------+------+------+------+
char *str2 = "Hello" (棧上的指標變數):
str2 (0x7FFEE...) ──┐
▼
0x400000 (唯讀段)
C 無字串類型,只有字元陣列與指標。搞清 儲存位置 (棧/堆/唯讀區)、所有權 (誰分配誰釋放)、可修改性,是 C 字串操作不出 Bug 的核心。