【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);  
}
相关推荐
Scott9999HH7 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社9 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海9 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖9 小时前
JDK 26 新特性详解
java·开发语言
马优晨10 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区12 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大12 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai13 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜13 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa13 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio