C ------ 字符串操作
我们上一次介绍一些关于内存操作的函数,今天我们来看看字符串操作的函数,如果有小伙伴对内存的操作还不了解,可以点击这里:
https://blog.csdn.net/qq_67693066/article/details/146564648?spm=1001.2014.3001.5502
strlen
strlen是计算一个字符串从开头到结束符(不包括结束符)的总体长度,这里strlen以'/0'作为标志,意思就是strlen会统计一个字符串的开头,到\0。然后统计这中间有多少个字符,之后作为这个字符串长度的结果。
cpp
#include<string.h>
int main()
{
const char* str = "abc\0defg";
printf("%d\n",strlen(str));
}
最后的结果为3:
strlen和sizeof的区别
同样的字符串,strlen和sizeof是不一样的:
cpp
#include<string.h>
int main()
{
const char* str = "abc\0defg";
char str1[] = "abc\0defg";
printf("%d\n",strlen(str));
printf("%d\n", sizeof(str1));
}
特性 | strlen(str) |
sizeof(str1) |
---|---|---|
作用对象 | 计算字符串长度(直到第一个 '\0' ) |
计算数组总大小(包括所有字符和隐含的 '\0' ) |
处理 '\0' |
遇到第一个 '\0' 停止 |
计算整个数组(包括中间的 '\0' 和末尾的 '\0' ) |
返回值 | 3 ("abc" 的长度) |
9 ('a','b','c','\0','d','e','f','g','\0' 共 9 字节) |
模拟实现strlen
cpp
size_t my_strlen(const char* str)
{
if (str == nullptr)
{
return 0;
}
const char* temp = str;
int count = 0;
while (*temp != '\0')
{
temp++;
}
return temp - str; //差值即为元素个数
}
char_traits::length
C++有个跟strlen很类似的,叫做char_traits::length:
cpp
#include<string.h>
#include<string>
int main()
{
const char* str = "abc\0defg";
printf("%d\n",strlen(str));
printf("%d\n", std::char_traits<char>::length(str));
}
特性 | char_traits::length |
strlen |
---|---|---|
语言 | C++(模板通用) | C(仅 char ) |
可扩展性 | 支持自定义字符类型 | 仅支持 char |
性能 | 可能被编译器优化(如内联) | 标准库实现 |
头文件 | <string> 或 <string_view> |
<cstring> |
strcpy
将 src 复制到 dest(包括 '\0'),需确保 dest 空间足够。返回值就是目标地址的指针:
cpp
#include<string.h>
#include<string>
int main()
{
const char* str = "abc\0defg";
char str1[] = "abc\0defg";
char str2[100];
char* check = strcpy(str2, str);
printf("str2的地址:%p\n", str2);
printf("strcpy返回值:%p\n", check);
printf("str2的内容:%s\n", str2);
}

模拟实现strcpy
cpp
#include<string.h>
#include<string>
char* my_strcpy(char* dest, const char* sour)
{
if (dest == nullptr || sour == nullptr)
{
return nullptr;
}
char* p = dest;
while ((*p++ = *sour++) != '\0');
return dest;
}
int main()
{
const char* str = "abc\0defg";
char str1[] = "abc\0defg";
char str2[100];
char* check = my_strcpy(str2, str);
printf("str2的地址:%p\n", str2);
printf("strcpy返回值:%p\n", check);
printf("str2的内容:%s\n", str2);
}
strncpy
strncpy是strcpy的安全版本:
这里注意strncpy如果目标空间不足,不会复制终止符\0到目标地址中,需要我们手动保证终止符存在
cpp
#include<string.h>
#include<string>
int main()
{
const char* str = "abcdefg";
char str1[] = "abc\0defg"; // 初始化为 "abc"(遇 '\0' 终止)
char str2[8]; // 足够容纳 "abcdefg" + '\0'
// 安全复制:限制最大长度为 sizeof(str2)-1
strncpy(str2, str, sizeof(str2) - 1);
str2[sizeof(str2) - 1] = '\0'; // 手动确保终止符
printf("str2的地址:%p\n", (void*)str2);
printf("strncpy返回值:%p\n", (void*)str2); // strncpy 返回 dest 指针
printf("str2的内容:%s\n", str2); // 输出 "abcdefg"
return 0;

strcpy和strnpy的区别
特性 | strcpy |
strncpy |
---|---|---|
终止符处理 | 总是复制 '\0' |
仅在 src 长度 < n 时添加 '\0' |
安全性 | 不安全(不检查长度) | 部分安全(需手动确保终止符) |
典型用途 | 已知目标空间足够时 | 需要限制复制长度时 |
strncpy模拟实现
strncpy的模拟实现主要关注到src < n 和src > n的情况:
cpp
char* my_strncpy(char* dest,const char* sour,int n)
{
if (dest == nullptr || sour == nullptr)
return nullptr;
char* p = dest;
while (n-- > 0 && (*p++ = *sour++) != '\0'); //循环执行,拷贝字符
while (n > 0)
{
*p++ = '\0';
n--;
}
return dest;
}
strcat
strcat的功能是将 source 字符串(src)的内容 追加 到 destination 字符串(dest)的末尾。
cpp
#include<string.h>
#include<string>
int main()
{
char str[100] = "abcdefgh";
strcat(str, "1234567");
printf("%s\n", str);
}

模拟实现
cpp
char* my_strcat(char* dest, const char* sour)
{
if (dest == nullptr || sour == nullptr)
return nullptr;
//开始拷贝,先找到\0的位置
char* p = dest;
while (*p != '\0')
p++;
//开始拷贝
while (*sour != '\0')
{
*p++ = *sour++;
}
*p = '\0';
return dest;
}
目标区域有重叠
手册里面说,strcat的使用源地址和目标地址不能有重叠 :
我们可以自己追加自己试试:
cpp
int main()
{
char str[100] = "abcdefgh";
strcat(str, str + 1);
printf("%s\n", str);
}
我们可以看到,出现无限循环复制 :
这是因为,strcat会找到\0,但是此时这种情况会把\0覆盖掉 :
strncat
strncat在原来strcat的基础上,传递了要追加的字符串的长度,并且会自动追加\0。
这样的话,有重叠区域也没有关系了:
cpp
#include<string.h>
int main()
{
char str[100] = "abcdefgh";
strncat(str, str + 1,strlen(str));
printf("%s\n", str);
}

模拟实现
cpp
char* my_strncat(char* dest, const char* sour, size_t size)
{
if (dest == nullptr || sour == nullptr)
return nullptr;
//寻找\0
char* p = dest;
while (*p != '\0')
p++;
//拷贝
while (size > 0 && *sour != '\0')
{
size--;
*p++ = *sour++;
}
*p = '\0';
return dest;
}
int main()
{
char str[100] = "abcdefgh";
my_strncat(str, str + 1,strlen(str));
printf("%s\n", str);
}

strcmp
strcmp用来比较两个字符串的大小:
cpp
#include<string.h>
int main()
{
char str1[100] = "we can't be friends";
char str2[100] = "we canr't be friends";
if (strcmp(str1, str2) > 0)
{
printf("str1 > str2");
}
else if (strcmp(str1, str2) == 0)
{
printf("str1 == str2");
}
else
{
printf("str1 < str2");
}
}

模拟实现
cpp
#include<string.h>
int my_strcmp(const char* str1, const char* str2)
{
if (str1 == nullptr && str2 == nullptr)
return 0;
if (str1 == nullptr || str2 == nullptr)
{
if (str1 == nullptr)
return *str2;
if (str2 == nullptr)
return *str1;
}
while (*str1 != '\0' && *str2 != '\0')
{
if (*str1 != *str2)
{
return *str1 - *str2;
}
str1++;
str2++;
}
return *str1 - *str2;
}
int main()
{
char str1[100] = "we can't be friends";
char str2[100] = "we canr't be friends";
if (my_strcmp(str1, nullptr) > 0)
{
printf("str1 > str2");
}
else if (my_strcmp(str1, str2) == 0)
{
printf("str1 == str2");
}
else
{
printf("str1 < str2");
}
}

strncmp
strncmp 是 C 标准库中的字符串比较函数,用于比较两个字符串的前 n 个字符
这里就不向大家演示了,大家可以自己下去试试。
strstr
strstr用于寻找字串:
strtok
strtok 是 C 标准库中的字符串分割函数,用于按照指定的分隔符将字符串拆分为多个子串:
cpp
#include<string.h>
#include<string>
int main()
{
char str[] = "apple,banana,orange";
char* token = strtok(str, ","); // 首次调用传入字符串
while (token != NULL) {
printf("Token: %s\n", token);
token = strtok(NULL, ","); // 后续调用传入NULL
}
return 0;
}

执行过程图解
初始字符串:
a p p l e , b a n a n a , o r a n g e \0
^
|
str
第一次strtok
后:
a p p l e \0 b a n a n a , o r a n g e \0
^ ^
| |
apple 下次从这里开始找
第二次strtok
后:
a p p l e \0 b a n a n a \0 o r a n g e \0
^ ^
| |
banana 下次从这里开始
strtok
会修改原字符串 (把逗号变成\0
)- 第一次调用传原始字符串,之后都传
NULL
- 就像用剪刀剪绳子:
- 第一次告诉你从哪里开始剪
- 之后每次接着上次剪断的地方继续
可以试着把代码改成这样观察:
c
char str[] = "apple,banana,orange";
printf("Before: %s\n", str); // 打印原始字符串
char *token = strtok(str, ",");
while (token != NULL) {
printf("Token: %s\n", token);
printf("Remaining: %s\n", token + strlen(token) + 1); // 看剩余部分
token = strtok(NULL, ",");
}
printf("After: %s\n", str); // 打印被修改后的字符串
这样就能看到字符串是如何被一步步修改的。