字串本質:以 '\0' 結尾的字元陣列
C 無內建字串型別,字串實為 char[] 且必須以空字元 '\0' (ASCII 0) 結尾。
char s[] = "hello"; 實際佔 6 位元組:h e l l o \0。
初始化方式總覽
| 寫法 | 合法性 | 備註 |
char a[6] = {'h','e','l','l','o','\0'}; | ✅ | 手動加 '\0' |
char b[] = "hello"; | ✅ | 編譯器自動加 '\0',長度 6 |
char c[10] = "hi"; | ✅ | 剩餘補 0 |
char d[5] = "hello"; | ❌ | 空間不足(需 6) |
char e[5] = {'h','e','l','l','o'}; | ⚠️ | 缺 '\0',非標準字串,strlen 會越界 |
字串陣列(二維/三維)
// 15 位學生,姓名上限 19 字元 + '\0'
char names[15][20];
// 初始化
char classA[2][10] = { "Jay", "Jim" };
char classB[2][2][10] = { { "Jay", "Jim" }, { "Tom", "Bob" } };
核心庫函式速查
| 函式 | 原型 | 行為 | 陷阱 |
| strlen | size_t strlen(const char *s) | 返回 '\0' 前字元數 | 不含 '\0';傳非字串會崩潰 |
| strcpy | char *strcpy(char *dest, const char *src) | 複製含 '\0' | dest 必須足夠大,否則緩衝區溢位 |
| strcat | char *strcat(char *dest, const char *src) | 拼接至 dest 末尾 | dest 需預留空間 |
| strcmp | int strcmp(const char *s1, const char *s2) | 逐字元比較 ASCII 差值 | 返回 0 表相等;>0 s1大;<0 s1小 |
| puts | int puts(const char *s) | 輸出字串並自動換行 | 僅能輸出單一字串 |
| gets | char *gets(char *s) | 讀整行含空格,遇 '\n' 止 | 已移除 (C11),極易溢位,禁用 |
| scanf | scanf("%s", s) | 讀單詞(跳過空白,遇空白止) | 無法讀含空格字串;需防溢位 %19s |
輸入函式選擇指南
- 單詞:
scanf("%19s", buf)(限寬防溢位)
- 整行(含空格):
fgets(buf, sizeof(buf), stdin) → 手動移除尾部 '\n'
- 混合使用:
scanf 後殘留 '\n' 會導致 fgets 讀到空行,需先清緩衝區。
字元 I/O
putchar('A'); // 輸出單字元
int ch = getchar(); // 讀單字元,含空格、Tab、換行
// 注意:getchar 讀到 '\n' 後需再按一次 Enter 才算輸入結束
常見錯誤與修正
| 錯誤代碼 | 問題 | 修正 |
char *p = "abc"; p[0] = 'X'; | 修改字串常量 → 段錯誤 | char p[] = "abc"; p[0] = 'X'; |
char a[10]; a = "hi"; | 陣列名為常量指標,不可賦值 | strcpy(a, "hi"); |
gets(buf); | 緩衝區溢位風險 | fgets(buf, sizeof(buf), stdin); |
scanf("%s", buf); // 輸入超長 | 緩衝區溢位 | scanf("%19s", buf); |
實戰片段:安全讀取一行並去除換行
char line[256];
if (fgets(line, sizeof(line), stdin)) {
size_t len = strlen(line);
if (len > 0 && line[len-1] == '\n') line[len-1] = '\0';
// line 現在是不含換行的純字串
}