5. 字符串处理(string.h):从 strlen 到 strtok
字符串处理,说白了就是 C 语言里最家常的活儿。我做了这么多年嵌入式开发,几乎每个项目都离不开 string.h 里的这些函数。今天咱们就把它们挨个捋一遍,把坑填平,把技巧说透。
5.1 strlen:别被长度骗了
strlen 返回的是字符串的长度,但不包括结尾的 '\0'。这个很多人一开始都会搞混。我记得刚入行时,有个同事给缓冲区分配了 strlen(str) 个字节,结果一拷贝就崩了——因为没给 '\0' 留位置。
'\0',如果字符串没有正确终止,它会一直跑下去,直到崩溃。
char str[] = "hello";
size_t len = strlen(str); // len = 5,不是 6
// 分配缓冲区时一定要 +1
char *buf = (char*)malloc(len + 1);
5.2 strcpy 与 strncpy:拷贝的陷阱
strcpy 用起来简单,但危险。它不做边界检查。我曾经在维护一个老项目时,看到一段代码用 strcpy 把用户输入拷到一个固定大小的数组里——结果就是经典的缓冲区溢出漏洞。
strncpy 看起来安全,但有个坑:它不会自动追加 '\0'。如果源字符串长度大于等于 n,目标字符串就不会以 '\0' 结尾。
'\0'。这步不能省。
char dst[10];
char *src = "hello world";
// 危险!
strcpy(dst, src); // 缓冲区溢出!
// 安全做法
strncpy(dst, src, sizeof(dst) - 1);
dst[sizeof(dst) - 1] = '\0'; // 手动保证终止
5.3 strcat 与 strncat:拼接的艺术
strcat 和 strcpy 一样,不做边界检查。你想想看,如果目标缓冲区不够大,它会一直往后写,覆盖掉别的数据。我见过一个 bug,就是 strcat 把栈上的返回地址给覆盖了,程序跑得莫名其妙。
strncat 比 strncpy 设计得合理一些——它总会追加 '\0'。但它有个特点:最多追加 n 个字符,不包括 '\0' 的位置。
char buf[16] = "hello";
char *suffix = " world!";
// 安全拼接
strncat(buf, suffix, sizeof(buf) - strlen(buf) - 1);
// 结果:buf = "hello world!"
sizeof(buf) - strlen(buf) - 1 作为 n 的值,这样最稳妥。
5.4 strcmp 与 strncmp:比较的学问
strcmp 按字典序比较两个字符串。返回 0 表示相等,负数表示第一个小于第二个,正数反之。很多人记不住这个返回值,我教你一招:strcmp(a, b) 的结果就是 a - b 的符号。
strncmp 只比较前 n 个字符。这在比较文件扩展名、协议前缀时特别有用。
if (strcmp(user_input, "admin") == 0) {
// 匹配成功
}
// 比较前 3 个字符
if (strncmp(filename, "tmp", 3) == 0) {
// 是临时文件
}
5.5 strstr 与 strtok:查找与分割
strstr 在字符串中查找子串,返回第一次出现的位置指针。找不到就返回 NULL。这个函数我经常用来做简单的协议解析。
strtok 是个有意思的函数。它用分隔符把字符串切成一段一段的。但它会修改原字符串——把分隔符替换成 '\0'。而且它内部维护了静态指针,所以不是线程安全的。
strtok_r(可重入版本)才解决。
char str[] = "apple,banana,orange";
char *token;
token = strtok(str, ",");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ",");
}
// 输出:
// apple
// banana
// orange
知识体系总览
下面这张图把 string.h 里这些函数的关系和用法总结了一下。我建议你把它存下来,写代码时对照着看。
避坑指南
最后,我把这些年踩过的坑总结一下:
- strlen 不包含 '\0'——分配缓冲区时记得 +1。
- strncpy 不自动加 '\0'——拷贝后手动补上。
- strcat 不做边界检查——用 strncat 并算好剩余空间。
- strtok 修改原字符串——如果需要保留原串,先拷贝一份。
- strtok 非线程安全——多线程环境用 strtok_r。
嗯,这些函数看着简单,但用不好真会出大问题。我建议你写代码时多留个心眼,尤其是处理用户输入或网络数据时,千万别偷懒。
公众号:蓝海资料掘金营,微信deep3321