size_t strlen ( const char * str );
下面给大家展示一下最简单的strlen函数的实现
//strlen函数的使用
#include<string.h>
int main()
{
char arr[] = "abcdef";
size_t len = strlen(arr);
printf("%zu", len);
return 0;
}
解释:arr数组里存放的是字符串“abcdef\0”,strlen计算的是‘\0’之前的字符的个数,所以strlen计算出来的结果就是6.
1.返回值类型:size_t size_t是无符号整型,这里要注意的是不能使用%d来打印strlen返回值,否则可能报警告

我们可以看到如果我们这里使用%d来打印它就报警告显示size_t转换到int可能丢失数据 2.strlen与sizeof的比较 函数作用示例strlen计算‘\0’之前字符的个数(不包含‘\0’) strlen(“abcde”) 输出结果:5 sizeof计算数组(字节)/字符串个数(包含‘\0’) sizeof(“abcde”) 输出结果:6
strlen函数使用时的误区:
这里给大家一段代码,大家思考一下它的输出结果是什么?
//strlen函数使用时的误区
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcdef";
if (strlen(arr1) - strlen(arr2) > 0)
printf("arr1>arr2\n");
else
printf("arr1<arr2\n");
return 0;
}这里我们明显看到arr1数组的元素个数要比arr2数组的元素个数要少,那么打印结果就是arr1<arr2咯?实践出真知,我们运行来看一下

这与我们分析的结果恰恰相反,可这是为什么呢?大家可以回归到上面的strlen函数使用时的注意事项,strlen函数的返回值类型是size_t,是一个无符号整型,那这里strlen(arr1)-strlen(arr2)可能是一个负数吗?很显然是不可能的,这里给大家解释一下:
按照正常逻辑 strlen(arr1)-strlen(arr2)计算出的结果是-2,而strlen的返回值类型是无符号整型,那么这里的-2要被当作无符号整型来处理,那怎么处理呢? -2的二进制原码:10000000 00000000 00000000 00000010 -2的二进制反码:111111111 111111111 111111111 111111101 -2的二进制补码:111111111 111111111 111111111 111111110 这里编译器要把-2当作无符号整型时,就会把-2的二级制的补码当作它的原码,所以这里strlen(arr1)-strlen(arr2)的二进制值为111111111 111111111 111111111 111111110可见是一个非常大的数,所以这里打印出来的结果恰恰与我们分析的相反
那如果我们非要通过作差来写这个代码呢?我们应该如何去写?
//改善后
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcd";
char arr2[] = "abcdef";
if ((int)strlen(arr1) - (int)strlen(arr2) > 0)
printf("arr1>arr2\n");
else
printf("arr1<arr2\n");
return 0;
}这里我就是通过强制类型转换来实现的,我们把size_t强制转化为int类型,而int是有符号整型,这里就不会出现上面一样的错误了,但是这里还是要强调一下,我们尽量不要去强制类型转化,因为又是强制类型转化可能导致数据的丢失。
方法一:计数器
//strlen函数的模拟实现
//方法一:计数器
//int my_strlen(const char*str)
//{
// int count = 0;
// while (*str)
// {
// count++;
// str++;
// }
// return count;
//}
//int main()
//{
// char str[] = "abcdef";
// int r = my_strlen(str);
// printf("%d", r);
// return 0;
//}方法二:指针-指针
//方法二:指针-指针
//int my_strlen(char* str)
//{
// assert(str);
// char* p = str;
// while (*p != '\0')
// {
// p++;
// }
// return p - str;
//}
//int main()
//{
// char str[] = "abcdef";
// int r = my_strlen(str);
// printf("%d", r);
// return 0;
//}方法三:递归(不创建临时变量的前提下):
//方法四:递归
//不能在函数内部创建临时变量,求字符串的长度
//size_t my_strlen(const char* str)
//{
// assert(str);
// if (*str != '\0')
// return 1 + my_strlen(str + 1);
// else
// return 0;
//}
//int main()
//{
// char str[] = "abcdef";
// size_t r = my_strlen(str);
// printf("%zu", r);
// return 0;
//}char* strcpy(char * destination, const char * source );
参数说明: char * destination是目标地址,const char * source是源头地址,strcpy函数的返回值类型是一个地址(指针)
下面给大家展示一下最简单的strcpy函数的实现
//strcpy
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
strcpy(arr2, arr1);
printf("%s\n", arr2);
return 0;
}
解释:strcpy(arr2,arr1)就是要把arr1数组里面的内容(包含‘\0’)拷贝到arr2里面.
1.源字符串里必须以‘\0’结尾: 如果源头数组里都没有‘\0’,那么到底要拷贝多少元素到数组arr2里面呢? 2.源字符串中的‘\0’也会拷贝到arr2数组里。 3.目标空间足够大: 目标空间必须足够大,以确保能存放源字符串。 4.目标空间必须可修改: 目标空间肯定是会被修改的,因为要把arr1数组里的内容拷贝进去,所以不能用const修饰arr2,但是可以用const来修饰arr1,来确保arr1数组里的内容不被修改。
#include<stdio.h>
#include<assert.h>
#include<string.h>
char* my_strcpy(char* des, const char* src)
{
char* ret = des;
assert(des && src);
while (*des++=*src++)
{
;
}
return ret;
}
int main()
{
char arr1[] = "abcdef";
char arr2[20] = "xxxxxxxxx";
char*r=my_strcpy(arr2, arr1);
printf("%s\n", r);
//printf("%s\n", arr2);
return 0;
}补充:assert断言,它的作用是用来检测是否有误,如果有错误,就会报错,并且会把错误的地方和原因呈现出来,如果没有错误,则这句语句就跳过。assert需要包含头文件<assert.h>
char* strcat(char * destination, const char * source );
参数说明: char * destination是目标地址,const char * source是源头地址,strcat函数的返回值类型是一个地址(指针)
下面给大家展示一下最简单的strcat函数的实现
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "Hello";
char arr2[] = "world";
char*r=strcat(arr1, arr2);
printf("%s\n", arr1);
printf("%s\n", r);
return 0;
}
1.源字符串必须以 '\0' 结束:不然拼接接到哪里为止不知道 2.目标字符串中也得有 \0:否则没办法知道追加从哪里开始拼接 3.目标空间必须有足够的大:这样目标字符串才能容得下拼接过后两个字符串长度的和 4.目标字符串必须为可修改的:这样strcat才有效果,源字符串可以添加const修饰
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* mystrcat(char* arr1, const char* arr2)
{
assert(arr1 && arr2);
char* p = arr1;
while (*arr1)
{
arr1++;
}
while ((*arr1++ = *arr2++))
{
;
}
return p;
}
int main()
{
char arr1[20] = "hello";
char arr2[] = "world";
char* ret = mystrcat(arr1, arr2);
printf("%s", ret);
return 0;
}#include<stdio.h>
#include<assert.h>
char* mystrcat(char* arr1, const char* arr2)
{
assert(arr1 && arr2);
char* p = arr1;
while (*arr1)
{
arr1++;
}//找到目标字符串中的\0,明确从那里开始追加。
while ((*arr1++ = *arr2++))
{
;
}
return p;
}
int main()
{
char arr1[20] = "hello";
char arr2[] = "world";
char* ret = mystrcat(arr1, arr2);
printf("%s", ret);
return 0;
}int strcmp(const char*str1,const char*str2);
参数说明和标准规定以及功能
const char*str1代表第一个字符串 const char*str2代表第二个字符串 返回值及规定: 返回值为int(整型) 如果第一个字符串大于第二个字符串,则返回值为大于0的数字 如果第一个字符串小于第二个字符串,则返回值为小于0的数字 如果第一个字符串等于第二个字符串,则返回值为等于0的数字 功能:用来比较str1和str2指向的字符串,从两个字符串的第一个字符开始比较,如果两个字符串的ASCALL码值相等,就继续比较下一个字符,直到遇到不相等的两个字符,或者字符串结束。
下面给大家展示一下最简单的strcmp函数的实现:
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abq";
int r = strcmp(arr1, arr2);
if (r > 0)
printf("arr1>arr2\n");
else if(r<0)
printf("arr1<arr2\n");
else
printf("arr1=arr2\n");
return 0;
}
#include<assert.h>
int my_strcmp(const char* str1, const char* str2)
{
assert(str1 && str2);
while (*str1 == *str2)
{
if (*str1 == '\0')
return 0;
str1++;
str2++;
}
if (*str1 > *str2)
return 1;
else
return -1;
}
int main()
{
char arr1[] = "abcdef";
char arr2[] = "abc";
int r = my_strcmp(arr1, arr2);
if (r > 0)
printf("arr1>arr2\n");
else if (r < 0)
printf("arr1<arr2\n");
else
printf("arr1=arr2\n");
return 0;
}char * strncpy ( char * destination, const char * source, size_t num );
我们观察发现strncpy函数与strcpy 函数的返回值一模一样,但是strncpy函数的函数参数比strcpy 函数参数多了一个,那这个参数的意义是什么呢?
这下大家应该对num参数的意义有了一定的了解了吧,那接下来我给大家展示一下最简单的strncpy函数的实现:
//strncpy
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdefg";
char arr2[20] = { 0 };
strncpy(arr2, arr1, 5);
printf("%s", arr2);
return 0;
}
那我们再思考一下如果拷贝时源字符串个数不够的话会怎么样呢?我们来做一个简单的测试
//strncpy
int main()
{
char arr1[] = "abc";
char arr2[] = "xxxxxxxxxxxxxxx";
strncpy(arr2, arr1, 5);
printf("%s", arr2);
return 0;
}
我们打开监视窗口发现当源字符串不够5个元素时,这里他就会自动把‘\0’补充直到5个为止,那如果提前遇到‘\0’又会是怎样的?我们再来做个简单的测试,监视它看一下结果:
//strncpy
int main()
{
char arr1[] = "abc\0def";
char arr2[] = "xxxxxxxxxxxxxxx";
strncpy(arr2, arr1, 5);
printf("%s", arr2);
return 0;
}
当提前遇到‘\0’时,strcpy就会当作源字符串已经结束了,就不会再拷贝'\0'后面的元素了,并且,不够的用'\0'来补充。
总结: strncpy相对于strcpy函数更加安全
#include<stdio.h>
#include<string.h>
#include<assert.h>
char* my_strncpy(char* arr1, const char* arr2, size_t num)
{
assert(arr1 && arr2);
char* p = arr1;
int i = 0;
for (i = 0;i < num && arr2[i];i++)
{
arr1[i] = arr2[i];
}
if (i < num)
{
arr1[i] = 0;
}
return p;
}
int main()
{
char arr1[20] = { 0 };
char arr2[] = "abcdefg";
char* ret = my_strncpy(arr1, arr2, 3);
printf("%s", ret);
return 0;
}char * strncat ( char * destination, const char * source, size_t num );
我们这里通过比较strncat函数发现strncat函数又只是比strcat多了一个参数,那我们通过类比猜测这个参数是不是也是限制它所要拼接的长度呢?
我们来看一个最简单的例子:
//strncat
int main()
{
char arr1[] = "abcdefgh";
char arr2[30] = "xxxxx";
strncat(arr2, arr1, 3);
printf("%s", arr2);
return 0;
}
这是时候我们思考一下,源头的字符串中的'\0'会被拼接过来吗?这时候我们做个简单的测试来看一下:
//strncat
int main()
{
char arr1[] = "abc";
char arr2[30] = "xxxxx\0xxxxxxx";
strncat(arr2, arr1, 3);
printf("%s", arr2);
return 0;
}
通过监视窗口,我们发现源字符串中的‘\0’确实被拷贝过来了,但是如果我们拼接两个字符的话,那‘\0’还会被拼接过来了吗?
//strncat
int main()
{
char arr1[] = "abc";
char arr2[30] = "xxxxx\0xxxxxxx";
strncat(arr2, arr1, 2);
printf("%s", arr2);
return 0;
}
通过监视窗口,我们发现当num的个数少于源头字符串元素个数时'\0'还是会被拼接过来。
那如果num>源字符串的个数时,会补更多的‘\0’吗?我们再来举个简单的例子:
//strncat
int main()
{
char arr1[] = "abc";
char arr2[30] = "xxxxx\0xxxxxxx";
strncat(arr2, arr1, 5);
printf("%s", arr2);
return 0;
}
我们通过监视窗口可以看到, 当num>源字符串的个数时,它并不会补两个‘\0’,那么我们总结一下吧。
总结:
#include<stdio.h>
#include<assert.h>
char* mystrncat(char* arr1, const char*arr2 , size_t num)
{
assert(arr1 && arr2);
char* p = arr1;
while (*arr1)
{
arr1++;
}
for (int i = 0;arr2[i] && i < num;i++)
{
arr1[i] = arr2[i];
}
if (num < 0)
{
arr1[i] = 0;
}
return p;
}
int main()
{
char arr1[20] = "hello";
char arr2[] = "world";
char* ret = mystrncat(arr1, arr2,3);
printf("%s", ret);
return 0;
}int strncmp ( const char * str1, const char * str2, size_t num );
我们通过类比strncmp函数,发现strncmp函数返回值类型与strcmp函数返回值类型相同,但是strncmp函数的参数比strcmp函数参数多一个,我们类比上面两个函数,就可以知道这个num参数的作用就是限制字符串的个数
作用:比较str1和str2的前num个字符,如果相等就继续往后比较,最多比较num个字母,如果提前发现不一样,就提前结束,大的字符所在的字符串大于另外一个。如果num个字符都相等,就是相等返回0.
规则:

如果第一个字符串大于第二个字符串,则返回值为大于0的数字 如果第一个字符串小于第二个字符串,则返回值为小于0的数字 如果第一个字符串等于第二个字符串,则返回值为等于0的数字
给大家展示一下最简单的strncmp函数的实现:
//strncmp
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdefgh";
char arr2[] = "abcdew";
int r = strncmp(arr2, arr1, 6);
if (r > 0)
printf(">");
else if(r<0)
printf("<");
else
printf("=");
return 0;
}
char * strstr ( const char * str1, const char * str2);
功能:strstr函数,查找str2指向的字符串在str1指针的字符串中第一次出现的位置。如果找到返回地址,找不到返回NULL
参数:str1指针,指向的被查找的字符串;str2指针,指向了要查找的字符串。
返回值:
strstr函数最简单的实现:
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "heheabcdefabcedf";
char arr2[] = "def";
char* p = strstr(arr1, arr2);
if (p != NULL)
printf("找到了,%s\n", p);
else
printf("没找到\n");
return 0;
}
#include<stdio.h>
#include<string.h>
char* my_strstr(const char* str1, const char* str2)
{
const char* p = str1;
const char* s1 = NULL;
const char* s2 = NULL;
if (*str2 == '\0')
return (char* )str1;
while (*p)
{
s1 = p;
s2 = str2;
while (*s1 && *s1 && *s1 == *s2)
{
s1++;
s2++;
}
if (*s2 == '\0')
return (char*)p;
p++;
}
return NULL;
}
int main()
{
char arr1[] = "heheabcdefabcedf";
char arr2[] = "def";
char* p = my_strstr(arr1, arr2);
if (p != NULL)
printf("找到了,%s\n", p);
else
printf("没找到\n");
return 0;
}char * strtok ( char * str, const char * sep);
参数说明:
功能:
使用规则:
strtok函数的最简单实现:
//strtok-->分割
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "asdfghjkl@qq.com";
char del[] = "@.";
char buf[20] = { 0 };
strcpy(buf, arr);
char* p = strtok(buf, del);
printf("%s\n", p); //asdfghjkl
p = strtok(NULL, del);
printf("%s\n", p); //qq
p = strtok(NULL, del);
printf("%s\n", p); //com
p = strtok(NULL, del);
printf("%s\n", p); //(null)
return 0;
}
我们如果这样写代码的话,如果字符串中有1000个分隔符,那我们是不是就要写1000行这种代码?是不是有点太“傻”了,那怎么样改进一下这个代码呢?
//高级版
#include<stdio.h>
#include<string.h>
int main()
{
char arr[] = "asdfghjkl@qq.com";
char del[] = "@.";
char buf[20] = { 0 };
strcpy(buf, arr);
char* p = NULL;
for (p = strtok(buf, del); p != NULL; p = strtok(NULL, del))
{
printf("%s\n", p);
}
return 0;
}
大家看打印结果是不是一模一样的啊?
char* strerror ( int errnum );
解释:strerror函数可以把参数部分错误码对应的错误信息的字符串地址返回来。在不同的系统和C语言标准库的实现中都规定了⼀些错误码,⼀般是放在 <errno.h> 这个头⽂件中说明的,C语言程序启动的时候就会使用一个全局的变量errno来记录程序的当前错误码,只不过程序启动的时候errno是0,表示没有错误,当我们在使⽤标准库中的函数的时候发⽣了某种错误,就会将对应 的错误码,存放在errno中,而⼀个错误码的数字是整数很难理解是什么意思,所以每⼀个错误码都 是有对应的错误信息的。strerror函数就可以将错误对应的错误信息字符串的地址返回。
参数: 这个错误码一般传递的是error这个变量的值,在C语言中有一个全局变量叫:errno,当库函数的调用发生错误的时候,就会将本次错误的错误码存放在errno这个变量中。
返回值:函数返回通过错误码得到的错误信息字符串的首字符的地址。
#include <errno.h>
#include <string.h>
#include <stdio.h>
int main()
{
int i = 0;
for (i = 0; i <= 10; i++) {
printf("%s\n", strerror(i));
}
return 0;
}
在Windows11+VS2022环境下输出的结果如下:
0:No error 1:Operation not permitted 2:No such file or directory 3:No such process 4:Interrupted function call 5:Input/output error 6:No such device or address 7:Arg list too long 8:Exec format error 9:Bad file descriptor
strerror也可以打开文件,如果文件不存在的话就会显示错误信息:
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
//C语言可以打开文件
//fopen
//如果以已读的形式打开文件,文件是必须要存在的,如果文件不存在,则打开文件失败
//fopen函数就会把错误码放在errno里
//同时函数会返回NULL
FILE* p = fopen("尺寸换米.exe", "r");
if (p == NULL)
{
printf("%s\n", strerror(errno));
return 1;
}
//读文件
//关闭文件
fclose(p);
p = NULL;
return 0;
}
这里就可以看到,当文件不存在的时候,编译器就会输出 No such file or directory表示文件不存在
也可以了解⼀下perror函数,perror函数相当于⼀次将上述代码中的第9行完成了,直接将错误信息 打印出来。perror函数打印完参数部分的字符串后,再打印一个冒号和一个空格,再打印错误信息。
//perror
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main()
{
//C语言可以打开文件
//fopen
//如果以已读的形式打开文件,文件是必须要存在的,如果文件不存在,则打开文件失败
//fopen函数就会把错误码放在errno里
//同时函数会返回NULL
FILE* p = fopen("尺寸换米.exe", "r");
if (p == NULL)
{
perror("exe");
//exe:错误信息
}
//读文件
//关闭文件
fclose(p);
p = NULL;
return 0;
}
最终的输出结果和桑面的其实是一样的
总结:到这里我给大家总结了10个最重要的字符串函数包括:strlen函数以及模拟实现、strcpy函数以及模拟实现、strcat函数以及模拟实现、strncpy函数以及模拟实现、strncat函数以及模拟实现、strncmp函数、strstr函数以及模拟实现、strtok函数以及改进、strerror函数,相信大家看了这篇文章能够有所收获,后续我还会分享更多的知识点,也欢迎大家能够进入我的主页浏览我写的文章,也欢迎在评论区留下你的问题,如果这篇文章对你有帮助的话,请给这篇文章点赞、收藏加关注,文章制作不易,谢谢大家的支持🌹🌹🌹