字符串处理函数
<string.h> 提供了丰富的字符串处理函数,包括复制、拼接、比较、查找等。这些函数是 C 语言字符串操作的核心工具,但许多函数不检查缓冲区大小,使用时需要格外小心。C99 引入的 snprintf 和长度受限的变体(strncpy、strncat)提供了更安全的替代方案。
复制函数
strcpy:复制字符串(不检查大小)
char dest[100];
strcpy(dest, "Hello"); /* 复制 "Hello" 到 dest */
/* 危险:如果 src 太长,dest 溢出 */
char dest2[5];
strcpy(dest2, "Hello World"); /* 越界! */
strncpy:限制长度的复制
char dest[100];
strncpy(dest, src, sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0'; /* 确保终止 */
strncpy 的陷阱:如果 src 长度 >= n,不会添加 \0!
memcpy:内存复制(不检查重叠)
char dest[100];
memcpy(dest, src, strlen(src) + 1); /* 复制包括 '\0' */
memmove:安全内存复制(处理重叠)
memmove(dest, src, n); /* 源和目的重叠时安全 */
拼接函数
strcat:拼接字符串(不检查大小)
char str[100] = "Hello";
strcat(str, " World"); /* str = "Hello World" */
/* 危险 */
char str2[10] = "Hello";
strcat(str2, " World"); /* 越界! */
strncat:限制长度的拼接
char str[100] = "Hello";
strncat(str, " World", sizeof(str) - strlen(str) - 1);
str[sizeof(str) - 1] = '\0'; /* 确保终止 */
比较函数
strcmp:比较字符串
int result = strcmp("abc", "def"); /* < 0 */
result = strcmp("abc", "abc"); /* == 0 */
result = strcmp("def", "abc"); /* > 0 */
/* 注意:不要与 0 用判等以外的运算 */
if (strcmp(a, b) == 0) /* 相等 */
if (strcmp(a, b) < 0) /* a < b */
strncmp:限制长度的比较
strncmp("abc", "abcdef", 3); /* == 0:只比较前 3 个字符 */
查找函数
strlen:字符串长度
size_t len = strlen("Hello"); /* 5 */
strchr:查找字符(首次出现)
char *p = strchr("Hello", 'l'); /* 指向第一个 'l' */
printf("%s\n", p); /* "llo" */
strrchr:查找字符(最后一次出现)
char *p = strrchr("Hello", 'l'); /* 指向最后一个 'l' */
strstr:查找子串
char *p = strstr("Hello World", "World"); /* 指向 "World" */
if (p != NULL)
printf("Found at position %zu\n", p - "Hello World");
strtok:分割字符串
char str[] = "Hello,World,C";
char *token = strtok(str, ",");
while (token != NULL) {
printf("%s\n", token);
token = strtok(NULL, ","); /* 继续分割 */
}
/* 输出:
Hello
World
C
*/
strtok 修改原字符串(插入 \0),且不是线程安全。C11 引入了 strtok_s(可选)。
其他函数
memset:填充内存
char str[100];
memset(str, 0, sizeof(str)); /* 全部置 0 */
memset(str, 'A', 10); /* 前 10 个字符设为 'A' */
strerror:错误信息字符串
#include <errno.h>
FILE *fp = fopen("nonexistent", "r");
if (fp == NULL)
printf("Error: %s\n", strerror(errno)); /* Error: No such file or directory */
常见错误
strcpy 溢出:
char dest[5];
strcpy(dest, "Hello World"); /* 越界! */
/* 安全替代 */
strncpy(dest, "Hello World", sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
strncpy 不保证 \0:
char dest[5];
strncpy(dest, "Hello World", 5); /* dest = "Hello",没有 '\0'! */
printf("%s\n", dest); /* 未定义行为 */
/* 正确 */
strncpy(dest, "Hello World", sizeof(dest) - 1);
dest[sizeof(dest) - 1] = '\0';
strcmp 返回值误用:
if (strcmp(a, b)) /* 相等时返回 0,条件为假 */
printf("Different\n");
else
printf("Equal\n"); /* 实际输出这个 */
/* 正确 */
if (strcmp(a, b) == 0)
printf("Equal\n");
strtok 修改原字符串:
char *str = "Hello,World"; /* 字符串字面量,只读 */
strtok(str, ","); /* 未定义行为!修改只读内存 */
/* 正确 */
char str[] = "Hello,World"; /* 可修改的数组 */
strtok(str, ",");
最佳实践
- 优先使用带长度限制的函数(
strncpy、strncat、snprintf) strncpy后手动添加\0- 不用
strcpy、strcat、sprintf处理外部输入 strcmp返回值与 0 比较,不要直接作为布尔值strtok只用于可修改的字符数组- 考虑使用更安全的库(如 BSD 的
strlcpy、strlcat)