5. 字符串处理(string.h):从 strlen 到 strtok

字符串处理,说白了就是 C 语言里最家常的活儿。我做了这么多年嵌入式开发,几乎每个项目都离不开 string.h 里的这些函数。今天咱们就把它们挨个捋一遍,把坑填平,把技巧说透。

5.1 strlen:别被长度骗了

strlen 返回的是字符串的长度,但不包括结尾的 '\0'。这个很多人一开始都会搞混。我记得刚入行时,有个同事给缓冲区分配了 strlen(str) 个字节,结果一拷贝就崩了——因为没给 '\0' 留位置。

注意:strlen 返回的是字符个数,不是字节数(对 ASCII 来说一样,但对多字节字符集就不一样了)。而且它遍历直到遇到 '\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' 结尾。

我的习惯:用 strncpy 后,手动在末尾加 '\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:拼接的艺术

strcatstrcpy 一样,不做边界检查。你想想看,如果目标缓冲区不够大,它会一直往后写,覆盖掉别的数据。我见过一个 bug,就是 strcat 把栈上的返回地址给覆盖了,程序跑得莫名其妙。

strncatstrncpy 设计得合理一些——它总会追加 '\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,结果两个线程互相干扰,解析出来的数据全乱了。后来改用 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 里这些函数的关系和用法总结了一下。我建议你把它存下来,写代码时对照着看。

string.h 字符串函数 strlen strcpy / strncpy strcat / strncat strcmp / strncmp strstr / strtok 不含 '\0' 缓冲区溢出 不自动加 '\0' 边界检查 自动加 '\0' 字典序比较 前 n 字符 子串查找 非线程安全 核心原则:永远假设缓冲区不够大,永远手动保证 '\0' 终止 ✅ 推荐使用 strncpy + 手动 '\0' ✅ 推荐使用 strncat + 空间计算 ✅ 推荐使用 strtok_r(可重入版)

避坑指南

最后,我把这些年踩过的坑总结一下:

  • strlen 不包含 '\0'——分配缓冲区时记得 +1。
  • strncpy 不自动加 '\0'——拷贝后手动补上。
  • strcat 不做边界检查——用 strncat 并算好剩余空间。
  • strtok 修改原字符串——如果需要保留原串,先拷贝一份。
  • strtok 非线程安全——多线程环境用 strtok_r。

嗯,这些函数看着简单,但用不好真会出大问题。我建议你写代码时多留个心眼,尤其是处理用户输入或网络数据时,千万别偷懒。


公众号:蓝海资料掘金营,微信deep3321