字符串处理

翻译整理自 cppreference - cstring。

1. C 字符串的本质

C 中没有真正的字符串类型,字符串就是以 \0(空字符)结尾的字符数组:

char s[] = "Hello";         // 长度 5,占用 6 字节(含 \0)
sizeof(s);                   // 6
strlen(s);                   // 5

2. 字符串字面量

char *a = "Hello";          // 字面量在只读段
char  b[] = "Hello";         // 拷贝到栈/数据段,可修改

字符串拼接:

char *s = "Hello, " "world";  // "Hello, world"

转义:

"\n\t\\\0\""

3. 字符串初始化

char s[10] = "abc";          // 'a','b','c','\0', 后续 0
char s[10] = {'a','b','c','\0'};
char s[4]   = "abc";

4. <string.h> 常用函数

size_t strlen(const char *s);
char  *strcpy(char *dest, const char *src);
char  *strncpy(char *dest, const char *src, size_t n);
char  *strcat(char *dest, const char *src);
char  *strncat(char *dest, const char *src, size_t n);
int    strcmp(const char *a, const char *b);
int    strncmp(const char *a, const char *b, size_t n);
char  *strchr(const char *s, int c);
char  *strstr(const char *s, const char *sub);
char  *strtok(char *s, const char *delim);

5. 拷贝字符串

char dst[10];
strcpy(dst, "hello");          // 安全:源串长度 < 10

strcpy 不检查缓冲区,易溢出。推荐 strncpy 或 snprintf:

strncpy(dst, src, sizeof(dst) - 1);
dst[sizeof(dst) - 1] = '\0';   // strncpy 不保证 \0 结尾

snprintf(dst, sizeof(dst), "%s", src);  // 推荐

6. 拼接

char buf[64] = "Hello, ";
strncat(buf, "world!", sizeof(buf) - strlen(buf) - 1);

snprintf(buf, sizeof(buf), "%s %s", "Hello,", "world!");

7. 比较

if (strcmp(a, b) == 0) {
    // 相等
}
// 返回值 < 0 表示 a < b,> 0 表示 a > b

大小写不敏感比较需自行实现或用 strcasecmp(POSIX):

#include <strings.h>
if (strcasecmp("Hello", "HELLO") == 0) { ... }

8. 查找

char *p = strchr("Hello, world", ',');
if (p) printf("位置 %ld\n", p - s);

char *sub = strstr("Hello, world", "world");
if (sub) printf("找到子串\n");

9. 分割 strtok

char line[] = "name,age,city";
char *tok = strtok(line, ",");
while (tok) {
    printf("%s\n", tok);
    tok = strtok(NULL, ",");
}

strtok 会修改原字符串,且非线程安全;推荐 strtok_r(POSIX)或 strtok_s(C11)。

10. 字符串转数字

int    n = atoi("123");
long   l = atol("99999");
double d = atof("3.14");

// 更安全:错误检查
char *end;
long v = strtol("0xff", &end, 16);   // 255

11. 字符串数组与 main 参数

int main(int argc, char *argv[]) {
    for (int i = 0; i < argc; i++) {
        printf("argv[%d] = %s\n", i, argv[i]);
    }
    return 0;
}

12. 长字符串处理

如果需要动态构造长字符串,建议用缓冲区或 snprintf:

char buf[256];
snprintf(buf, sizeof(buf),
    "name=%s age=%d score=%.2f",
    name, age, score);

小结

C 字符串全靠库函数和约定维护,注意 \0 结尾与缓冲区大小就能避免绝大多数 bug。