【C语言】字符串处理函数

C 语言的字符串处理函数 全部定义在 <string.h> 头文件中,专门用于处理以 \0 结尾的字符数组(C 语言字符串)。

1、字符串长度

1.1 strlen

c 复制代码
size_t strlen(const char *str);
  • 作用 :计算字符串有效长度 (不含末尾的 \0
  • 参数:目标字符串
  • 返回值:无符号整数(长度)
  • 示例
c 复制代码
char str1[] = "Hello";  
char str2[] = "Hello\0World";  
char *str3 = "Hello";  
  
printf("str1: %s\n", str1); // prints "Hello"  
printf("str2: %s\n", str2); // prints "Hello"  
  
// sizeof()返回的是数组的总大小,包括结尾的'\0'  
printf("str1: %llu\n", sizeof(str1)); // prints 6  
printf("str2: %llu\n", sizeof(str2)); // prints 12  
  
// strlen() 返回的是字符串的长度,不包括结尾的'\0', 遇到'\0'会停止计算  
printf("str1: %llu\n", strlen(str1)); // prints 5  
printf("str2: %llu\n", strlen(str2)); // prints 5  
  
// 字符串常量str3是只读的,不能修改  
printf("str1: %llu\n", strlen(str1));

! NOTE

注意strlen 遇到 \0 就停止,不计入结束符

如果是数组,可以使用sizeof计算数组的长度,包含尾部的'\0'

2、字符串复制

2.1 strcpy

c 复制代码
char *strcpy(char *dest, const char *src);
  • 作用不安全 字符串复制(把 src 复制到 dest,包含 \0
  • 参数:目标、源
  • 返回值:指向 dest 的指针
  • 示例
c 复制代码
char src[] = "Hello, world!";  
char dest[20];  
  
strcpy(dest, src);  
printf("%s\n", dest);   // prints "Hello, world!"

!NOTE

  • 如果 dest 空间不足,会内存越界。因此需要确保dest空间能够容纳src字符串长度。
  • 遇到'\0'字符结束拷贝。

2.2 strncpy

c 复制代码
char *strncpy(char *dest, const char *src, size_t n);
  • 作用安全复制,最多复制 n 个字符
  • 注意
    • 不会自动补 \0!必须手动加
    • 源长度 < n:剩余空间填 \0
    • 遇到\0会结束;
  • 示例
c 复制代码
// 遇到\0会结束
char src[] = "Hello\0, world!";  
char dest[20];  
  
strncpy(dest, src, 10);  
  
printf("%s\n", dest);   // 应该输出 "Hello, world!" 但实际上会输出 "Hello"
c 复制代码
// 不会自动加\0
char src[] = "Hello, world!";  
char dest[20] = "aaaaaaaaaa";  
  
strncpy(dest, src, 5);  
printf("%s\n", dest);   // prints "Helloaaaaa"  
  
// 需要手动加\0  
dest[5] = '\0';  
printf("%s\n", dest);   // prints "Hello"
c 复制代码
// 最安全的做法
char src[] = "Hello, world!";  
char dest[20] = "aaaaaaaaaaaaaaa";  
  
// 最安全的拷贝方法,防止移除,并且最后补了0
strncpy(dest, src, sizeof(src));  
printf("%s\n", dest);

3、字符串拼接

3.1 strcat

c 复制代码
char *strcat(char *dest, const char *src);
  • 作用:把 src 拼接到 dest 末尾
  • 危险:不检查空间,容易越界
  • 推荐使用:[[字符串处理函数#3.2 strncat|strncat]]

3.2 strncat

c 复制代码
char *strncat(char *dest, const char *src, size_t n);
  • 作用 :最多拼接 n 个字符,自动补 \0
  • 推荐使用
c 复制代码
char src[10] = "World";  
char dst[12] = "Hello";  
  
strncat(dst, src, 5);  
  
printf("%s\n", dst);  // HelloWorld

4、字符串比较

4.1 strcmp

c 复制代码
int strcmp(const char *s1, const char *s2);
  • 作用按 ASCII 码逐字符比较
  • 返回值
    • 0:相等
    • <0:s1 < s2
    • 0:s1 > s2

  • 示例
c 复制代码
char str1[] = "aaa";  
char str2[] = "bbb";  
char str3[] = "aaa";  
  
// 比较两个字符串的大小, 若str1 < str2, 则返回负值;  
// 若str1 > str2, 则返回正值;  
// 若str1 == str2, 则返回0.  
// 注意: 比较时, 若两个字符串相等, 则返回0;  
// 若不相等, 则返回第一个不相同字符的ASCII码之差.  
int ret = strcmp(str1, str2);  
if (ret < 0) {  
    printf("str1 < str2\n");  
}  
else if (ret > 0) {  
    printf("str1 > str2\n");  
}  
else {  
    printf("str1 == str2\n");  
}  
printf("str2 == str1 ? %d\n", ret); // 返回第一个不相同字符的ASCII码之差 -1
ret = strcmp(str1, str3);  
printf("str1 == str3? %d\n", ret);  // 返回0, 因为两个字符串相等

4.2 strncmp

c 复制代码
int strncmp(const char *s1, const char *s2, size_t n);

作用:只比较前 n 个字符

5、字符串查找

5.1 strchr

c 复制代码
char *strchr(const char *str, int c);
  • 作用 :查找字符 c 第一次出现的位置
  • 返回值 :找到返回地址,没找到返回 NULL
  • 示例
c 复制代码
char str[20] = "hello world";  
  
char *p = strchr(str, 'l');  
// 找不到会返回NULL,否则返回指向第一个匹配字符的指针  
// 先判断是否为NULL,再使用指针指向的字符串  
if (p != NULL) {  
    printf("%s\n", p);  // prints "llo world"  
}

5.2 strrchr

c 复制代码
char *strrchr(const char *str, int c);

作用 :查找字符 c 最后一次出现的位置相当于反向查找一个

c 复制代码
char str[20] = "hello world";  
  
char *p = strrchr(str, 'l');  
// 找不到会返回NULL,否则返回指向第一个匹配字符的指针  
// 先判断是否为NULL,再使用指针指向的字符串  
if (p != NULL) {  
    printf("%s\n", p);  // prints "ld";  
}

5.3 strstr

c 复制代码
char *strstr(const char *haystack, const char *needle);

作用查找子串(比较常用)

c 复制代码
char str[20] = "hello world";  
  
// strstr 查找子串,返回子串的起始地址,找不到返回NULL  
char* p = strstr(str, "ll");  
if (p != NULL) {  
    printf("%s\n", p); // ll world  
}

6、字符串分割

6.1 strtok

c 复制代码
char *strtok(char *str, const char *delim);
  • 作用 :按分隔符切割字符串
  • 特点
    • 会修改原字符串(多线程不安全)
    • 第一次传字符串,后续传 NULL
  • 示例
c 复制代码
char str[20] = "He,llo;world!";  
  
// strtok() 函数用于分割字符串,第一个参数是要处理的字符串,第二个参数是分隔符  
// 分割符可以是多个字符,例如 ",;" 表示以逗号或分号分割字符串  
char *p = strtok(str, ",;");  
while (p != NULL) {  
    printf("%s\n", p);  
    // 继续分割字符串,直到没有更多的分隔符为止,传入NULL作为第一个参数  
    p = strtok(NULL, ",;");  
}

!NOTE

strtok 函数内部使用了静态全局变量保存切割上下文,所有线程共享该变量,多线程并发调用时会发生数据竞争,现场被互相覆盖,导致结果不可预期。

使用线程安全的可重入版本 strtok_r(Linux)/strtok_s(Windows)。

7、内存操作(通用,不限于字符串)

内存操作函数不关心\0,都是按照字节操作内存。

7.1 memset

c 复制代码
void *memset(void *ptr, int value, size_t num);

作用:批量赋值(常用于清空数组、或内存区域清0)

c 复制代码
char str[10] = "Hello";  
// 这里没有初始化,数组的值是不确定的  
int arr[10];  
 
memset(str, '\0', 10);  
 
printf("%s\n", str);  
 
// 初始化数组为0  
memset(arr, 0, sizeof(int) * 10);  
for (int i = 0; i < 10; i++) {  
   printf("%d\n", arr[i]); // 输出0  
}

7.2 memcpy

c 复制代码
void *memcpy(void *dest, const void *src, size_t n);

作用:内存拷贝(比 strncpy 更通用)

  • 字节 拷贝,不关心 \0,不限于字符串
  • 拷贝 n 字节
  • 返回 dest
    注意:
  • 不保证处理内存重叠
    • srcdest 指向同一块数组,且区间有重叠:
  • 行为未定义(可能乱、可能正常、可能崩溃)
  • 通常实现是从低地址往高地址逐字节拷贝
c 复制代码
char src[] = "Hello, world!";  
char dest[20] = {0};  
  
memcpy(dest, src, strlen(src));  
printf("%s\n", dest);  
  
memcpy(src, src, strlen(src));  // 错误的使用, 会导致未定义行为  
printf("%s\n", src);

! NOTE

memcpy 可能覆盖还没读的数据 → 结果错乱

memmove 会从后往前拷贝,保证正确

7.3 memmove

c 复制代码
void *memmove(void *dest, const void *src, size_t n);

作用可处理内存重叠的拷贝(更安全)

  • 保证安全处理内存重叠
  • 内部会自动判断地址高低,选择正向 / 反向拷贝
  • 结果永远可预期
c 复制代码
char src[] = "Hello, world!";  
char dest[20] = {0};  
  
memmove(dest, src, strlen(src));  
printf("%s\n", dest);  
  
// 这种情况建议使用memmove,因为src和dest可能重叠  
memmove(src, src, strlen(src));  
printf("%s\n", src);

7.4 memcmp

c 复制代码
int memcmp(const void *s1, const void *s2, size_t n);

内存比较,比较的是每一个字节的二进制数据。

7.5 memchr

c 复制代码
void *memchr(const void *str, int c, size_t n);

内存中查找字节

8、字符串转换

这些函数都在 <stdlib.h> 头文件里。

8.1 atoi \ atol \ atoll

数字字符串 转成 -> 整数 \ long \ long long

使用注意:

atoi和atof两个函数规则完全一样

  1. 跳过开头的空白符(空格、换行、制表符)
  2. 识别正负号
  3. 连续读取数字 ,直到遇到非数字字符停止
  4. 无法转换 → 返回 0
c 复制代码
char str1[10] = "123";  
char str2[10] = "a456";  
  
int ret = atoi(str1);  
printf("%d\n", ret);  
// 转换失败,返回0  
ret = atoi(str2);  
printf("%d\n", ret);

8.2 atof

字符串到浮点数

c 复制代码
char str1[10] = "0.123";  
float ret = atof(str1);  
printf("%f\n", ret); // 0.123000

9、字符大小写转换

需要包含#include <ctype.h>

c 复制代码
printf("%c\n", tolower('A'));   // a  
printf("%c\n", toupper('a'));   // A

10、扩展

10.1 strtok_s和strtok_r使用

  • strtok_r:Linux、Unix、macOS 用(POSIX 标准)
  • strtok_s:Windows 用(C11 标准)
  • 功能完全一样 :自己传入指针保存切割现场,多线程安全、可重入、可嵌套
c 复制代码
char *strtok_r( char *str, const char *delim, char **saveptr );
  • saveptr自己定义的指针,用来保存切割位置
  • 每个切割任务独立一个 saveptr → 完全安全
c 复制代码
char *strtok_s( char *str, const char *delim, char **context );
  • 和 strtok_r 一模一样,只是第三个参数名字叫 context
  • 功能、用法、原理 100% 相同

! 说明

strtok 内部用 静态全局变量 保存位置 → 所有线程共享

strtok_r / strtok_s 由你自己传入指针保存位置 → 每个线程独立一份,互不干扰

c 复制代码
char str[20] = "He,llo;Wor,ld";  
char *savepoint = str;  // 保存下一次调用strtok_s的起始位置  
  
// 第一次传入字符串  
char *p = strtok_s(savepoint, ",;", &savepoint);  
while (p != NULL) {  
    printf("%s\n", p);  
    // 第二次传入NULL,继续分割字符串  
    p = strtok_s(NULL, ",;", &savepoint);  
}
相关推荐
Mr_Xuhhh8 分钟前
深入理解Java抽象类与接口:从概念到实战
java·开发语言
萝卜白菜。30 分钟前
TongWeb7.0相同的类指明加载顺序
开发语言·python·pycharm
wb0430720130 分钟前
使用 Java 开发 MCP 服务并发布到 Maven 中央仓库完整指南
java·开发语言·spring boot·ai·maven
Rsun0455131 分钟前
设计模式应该怎么学
java·开发语言·设计模式
良木生香1 小时前
【C++初阶】:C++类和对象(下):构造函数promax & 类型转换 & static & 友元 & 内部类 & 匿名对象 & 超级优化
c语言·开发语言·c++
5系暗夜孤魂1 小时前
系统越复杂,越需要“边界感”:从 Java 体系理解大型工程的可维护性本质
java·开发语言
无巧不成书02182 小时前
C语言零基础速通指南 | 1小时从入门到跑通完整项目
c语言·开发语言·编程实战·c语言入门·零基础编程·c语言速通
三雷科技2 小时前
使用 `dlopen` 动态加载 `.so` 文件
开发语言·c++·算法
wellc2 小时前
java进阶知识点
java·开发语言
听风吹等浪起2 小时前
用Python和Pygame从零实现坦克大战
开发语言·python·pygame