分享几个常见的字符串函数及其易错点
目录
1.strlen
直接上代码
#include<stdio.h>
#include<string.h>
int main()
{
int len = strlen("abcdef");
printf("%d", len);
return 0;
}
相信大家对strlen已经非常熟悉,字符串是以'\0'作为字符串的结束标志,strlen函数返回的是在字符串中'\0'前面出现的字符串个数,不包括'\0',但是要注意的是这个函数的返回值是size_t,也就是我们说的无符号数。
要测试也很简单,只需在字符串中加入\0就能验证是否真的会以\0结束
int main()
{
int len = strlen("abc\0abc");
printf("%d", len);
return 0;
}
那么毫无疑问运行结果是3
还需要注意的是strlen是一个函数,参数指向的字符串必须必须要以'\0'结束,否则无法计算准确的结果
如下我们给出一段没有\0的字符串
int main()
{
char arr[3] = { 'a','b','c' };
int len = strlen(arr);
printf("%d", len);
return 0;
}
我的arr字符数组中没有存放\0,所以电脑会给出一个随机值
那么我们上面也说过strlen的返回值是一个size_t(无符号)类型的数那么下面这段代码运行的结果是什么呢
int main()
{
if (strlen("abc") - strlen("abcdef") > 0)
{
printf(">\n");
}
else
{
printf("<=\n");
}
return 0;
}
那么你答对了吗
strlen("abc")的结果是3,strlen("abcdef")的结果是6,他们,他们按照数学的逻辑运算结果是-3,但是我们再再强调strlen的返回值是一个无符号数,所以-3将会以补码的形式输出并且是一个正数,所以一定是">",
然后就是怎么自定义函数模拟实现strlen函数
有计数器的方式,递归,指针减指针的方式;
下面我们一一实现
首先是计数器
int my_strlen(const char* str)
{
}
int main()
{
char arr[] = "bit";
int len=my_strlen(arr);
printf("%d", len);
return 0;
}
这里我们要说明的一点是是自定义函数的返回类型,上面说的是size_t类型,但是到了我们手里怎么酒席成了int类型了呢?我们还是拿上面的代码举例啊
库函数中的strlen的返回值是无符号整形但是经过上图的计算本来结果应该是-3,但是计算机中的补码会换算成一个非常大的正整数,这时就要考虑我们使用的目的和环境了,计算机考虑的可能是既然要计算长度,那么长度就不会有小于0的情况,所以便使用了无符号整形的类型,但是按照我们做正常计算的逻辑来算3-6就应该等于-3,所以两种类型都各有各自的说法。至于再形参中前面加上const是为了保护我们的arr数组中的内容不被更改;
接下来是函数体内容
int my_strlen(const char* str)
{
int count = 0;
while(*str != '\0')
{
count++;
str++;
}
return count;
}
计数器方法我们当然要定义一个变量count当作计数器,从代码中我们不难看出str是char*类型的指针,指向arr数组中的第一个元素的地址,str是char*类型的指针,指向arr数组中的第一个元素的地址,基础的数组传参要记清楚,往下走,在while循环中对str指针进行解应用,当这个指针指向的地址中的元素不为'\0'的时候,那就给count++;并且要将指针指向的地址向后一位,一直进行循环,直到str指针解引用为'\0'时停止循环;
好让我们将代码运行起来,代码没有任何问题
这就用计数法自定义函数实现了strlen库函数
接下来使用递归的方法模拟实现strlen
递归实现的方式可以不用创建临时变量
我们还是用上面的main函数来举例
int main()
{
char arr[] = "bit";
int len=my_strlen(arr);
printf("%d", len);
return 0;
}
其实也很简单,当我们看到字符数组arr中的元素时,"bit"中的b不为'\0',说明他不是结尾,他这个字符串的长度就是1+“it”的长度,那“it”的长度又可以看成1+“t“的长度,所以更长的字符串我们就可以以此类推
代码如下
int my_strlen(const char* str)
{
if (*str != '\0')
return 1 + my_strlen(str + 1);
else
return 0;
}
进入函数体首先要判断传入的数组的首元素是否为'\0',不为'\0'的话就继续让1+指针下标为下一个元素的地址,直到不进入if的判断条件
让代码走起来
结果依然是3,所以函数递归方法可以不用创建临时变量;
第三种方法是指针减去指针的方法;思路也非常简单就是末尾元素的地址再减去首元素的地址就可以求出字符串长度
int my_strlen(const char* str)
{
char* start = str;//start里面装的是首元素a的地址
while (*str != '\0')
{
str++;
}
return str - start;
}
在函数中定义一个char*类型的指针存放传过来的参数中的首地址元素,和上面一样进入循环后让str++,要注意的是只是str++,一直加到\0为止,而start指针的位置没有改变,所以循环结束后str指针指向字符串的末尾,start是指向开始的位置,所以用末尾减去首地址也是字符串的长度
结果和上面两种方式是一样的。
2.strcpy
也就是字符串拷贝的意思
直接上代码,同样也要包含头文件<string.h>
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[] = "abcdef";
char arr2[20] = { 0 };
strcpy(arr2, arr1);
printf("%s", arr2);
return 0;
}
strcpy的作用就是将一个数组的内容拷贝到另一个数组中去
我们去官网查看一下这个函数的基本情况
这个函数有两个指针类型的参数,前面的参数中destination是目的地的意思,后面参数中source是源头的 意思,所以这个函数的意思就是将后面的参数的内容拷贝到前一个参数中去,那我们运行一下上面的代码
那很明显arr2数组中的内容变成了arr1数组中的内容;
那数组能不能像交换数字一样交换数组的内容呢?
是坚决不行的
这样写就会报错,这是语法的错误,我们千万不能这样写,要遵循语法的规则,只能使用strcpy。
那同样按照第一个函数的方法代码如下
int main()
{
char arr1[] = "abc\0def";
char arr2[20] = { 0 };
strcpy(arr2, arr1);
printf("%s", arr2);
return 0;
}
结果会是怎么样呢?
我们只将abc拷贝到了arr2数组当中去,当然\0也会拷贝到新的字符串数组中去
所以当我们的原字符串中没有'\0'时
int main()
{
char arr1[3] = {'a','b','c'};
char arr2[20] = { 0 };
strcpy(arr2, arr1);
printf("%s", arr2);
return 0;
}
就会出现随机值
所以源字符串必须以\0来结束 ,并且会将\0拷贝到目标空间里面去;
还需要注意的是目标数组的空间必须要足够大,以确保能够存放源字符串的内容
#include<stdio.h>
#include<string.h>
int main()
{
char arr1[20] = "asdfghjkl";
char arr2[5] = { 0 };
strcpy(arr2, arr1);
printf("%s", arr2);
return 0;
}
当我们把代码写成这样时很明显目标数组不够大,就会出现栈溢出的问题
还有就是要注意目标空间要可以被修改
如果我写了下面这样一段代码,运行后就会发现
int main()
{
char* p = "abcdef";
char arr2[10] = "hehe";
strcpy(p, arr2);
printf("%s\n",p);
return 0;
}
写入时两个位置冲突,所以写入时目标空间必须可以被修改。
接下来就是strcpy函数的自定义模拟实现
char* my_strlen(char* dest, const char* src)
{
char* ret=dest;//函数要返回首元素地址,我们不妨将首元素地址先存起来
assert(dest &&src);//断言确保两个地址相等
while (*dest++ = *src++)//判段将两个指针解应用后的值相等,并且自加至\0 ,当自加至\0的时候,while(0),于是跳出循环
{
;
}
return ret;//函数需要返回首元素地址,将开始存下来的首元素地址返回
}
还是要注意函数的返回类型和所传递的参数。
这样即做到了\0的拷贝,也做到了遇到\0停止。
那么这样也可以实现数组拷贝;
3.strcat
上面的strcpy只能将内容覆盖,现在我们要讲解的是追加函数,也就是说在字符串后面在添加字符串,这时候就用到strcat函数
我们在官网上查找一下strcat函数的返回类型和参数,可以看到和strcpy的样子是一样的,同样要包含<string>头文件
用代码演示他的用法
#define _CRT_SECURE_NO_WARNINGS 1
#include<string.h>
#include<stdio.h>
int main()
{
char arr1[20] = "hello ";
char arr2[20] = "world";
strcat(arr1, arr2);
printf("%s\n", arr1);
return 0;
}
可以看到arr2数组中的内容通过追加函数追加到了arr1数组的后面
我们经过调试来深入的研究一下两个数组中的内容
我们可以看到arr1数组中除了hello以外还有一个空格和一个\0,这个空格是我们当时手动输入的,那函数追加是怎么进行追加呢?再次敲F10,让代码走下去,再次观察arr1中的内容
走!
注意看,红色是走一步后改变的值,对比上面我们发现是从arr1数组中的\0开始追加的
那么我们在追加的函数中是否也必须有'\0'呢?
经过调试我们发现要追加的数组中也必须有'\0',否则就会报错
所以目标空间必须可以被修改,并且空间必须够大,能够放下字符内容,这些条件都是不能缺少的。
接下来就是模拟实现strcat函数
同样先处理函数返回类型和函数参数的问题
char* my_strcat(char* dest,const char* src)
{
}
和上面的一样,函数的返回类型是char*,要把后面的内容加到前面去,所以后面的内容不用改变,要用const修饰。
接下来就是函数体
上面讲到过源字符串是加在目标空间的第一个\0后的,所以我们的思路只需先找到目标空间的第一个\0处,再用strcpy将其拷贝进去即可;
char* my_strcat(char* dest,const char* src)
{
while(*dest!='\0')//这个while循环用来寻找目标空间的第一个\0
{
dest++;
}
while(*dest++=*src++)//再用strcpy将其\0后的内容进行拷贝
{
;
}
}
但是仔细观察我们会发现返回值要是首元素地址,但是再函数体中首元素地址已经被++了很多次,所以我们要将首元素地址在开始先进行一次保存,代码如下
char* my_strcat(char* dest,const char* src)
{
char* ret=dest;
while(*dest!='\0')//这个while循环用来寻找目标空间的第一个\0
{
dest++;
}
while(*dest++=*src++)//再用strcpy将其\0后的内容进行拷贝
{
;
}
return ret;
}
这样就是一个自定义函数模拟strcat的完全体
代码运行起来
完全没有问题
希望以上内容对你有所帮助