C 字符串常见坑
更新时间:2026-08-26。本文是
languages/c/主题入门层第 30 篇。字符串专题的"实战课":前几篇的知识点散在各处,这一篇把它们聚成一份排错清单——遇到字符串 bug,照着这张表对一遍。
本文要回答的问题
- 字符串程序最常见的十个错误是什么?
- 每个错误的现象、原因、修法分别是什么?
- 怎么快速定位字符串 bug?
一、十大坑速查表
| # | 错误 | 现象 | 修法 |
|---|---|---|---|
| 1 | 数组没留 \0 空间 | 越界写 / 乱码 | 容量 = 长度 + 1 |
| 2 | strcpy 目标太小 | 缓冲区溢出 | snprintf + sizeof |
| 3 | strcmp 结果当布尔 | 判断结果相反 | strcmp(a,b) == 0 |
| 4 | 修改字符串字面量 | 段错误 | 复制到数组再改 |
| 5 | strlen 用在没 \0 的数组 | 越界读垃圾长度 | 保证 \0 结尾 |
| 6 | 数组传参后 sizeof | 拿到 8(指针) | 另传长度 |
| 7 | strcat 剩余空间不够 | 溢出 | 先算剩余容量 |
| 8 | strncpy 丢 \0 | 无终点字符串 | 手动补 \0 |
| 9 | 混淆 'a' 和 "a" | 类型/编译错误 | 字符单引号,字符串双引号 |
| 10 | 下标越界(s[i] 超出长度) | UB / 崩溃 | 先算好边界 |
二、前四个坑展开看
坑 1 + 坑 5 是同一根源:\0。\0 既是"结尾标志",也是"内存占用"。忘了它,strlen 越界、strcpy 越界,全是连锁反应。
坑 2:strcpy 不信"目标够大",它只认源串长度:
c
char buf[4];
strcpy(buf, "hello"); // 写 6 字节进 4 字节的坑 → 溢出坑 3:strcmp("a", "b") 返回值是负数(非 0),直接放 if 里当"相等"判断就反了。
坑 4:char *s = "hello"; s[0] = 'H'; 在只读内存上写——Linux 下就是段错误(Windows 上可能"碰巧"不崩,更阴险)。
三、'a' 与 "a":单双引号的战争
c
char c = 'a'; // 字符:值 97,占 1 字节
char *s = "a"; // 字符串:'a' '\0',占 2 字节 + 指针
// 常见错误:
strcmp(c, "a") // 把 char 当字符串传,类型错误/崩溃'a' | "a" | |
|---|---|---|
| 类型 | char(值 97) | char[2]('a','\0') |
| 大小 | 1 字节 | 2 字节 |
| 用途 | 单个字符 | 字符串 |
四、怎么快速排查字符串 bug
第一步:开 ASan。开发期 -fsanitize=address 编译,越界和溢出一跑就报,比肉眼快十倍:
bash
gcc -O0 -g -fsanitize=address -o prog prog.c && ./prog第二步:打印调试。怀疑哪一段,就在附近加打印确认边界:
c
printf("len=%zu size=%zu\n", strlen(s), sizeof(s));第三步:gdb 看内存。x/16bx s 在 gdb 里看字符串前后 16 字节,\0 在不在、越界写到了哪,一目了然:
text
(gdb) x/16bx s
0x7fffffffe340: 0x68 0x65 0x6c 0x6c 0x6f 0x00 0x00 ...
h e l l o \0五、与本站主线衔接
- 字符串 bug 的自动化检测(ASan、Valgrind),见 crash 主题;
- 安全的字符串 API 与 C11 Annex K 的争论,见缓冲区溢出与安全;
- 字符串性能(大量 strcat 的低效),见高手层字符串性能。
一句话总结
字符串十大坑的根源就三个:\0(结尾与占用)、容量(写入前先算)、字面量(只读别改)——strcmp 要 == 0、'a' 和 "a" 是两种东西、sizeof 传参后失效;排错用 ASan 开路,gdb 看内存收尾。