首先我们先来观察一下库函数strcpy去实现字符串拷贝的功能
int main(void)
{
char str1[10] = "xxxxxxxxx";
char str2[] = "hello";
strcpy(str1, str2);
printf("%s\n", str1);
return 0;
}
\0
一起拷贝过去。所以对于目标字符串我没有初始化为0就是为了看出拷贝完成的工作好,看完了库函数的实现之后,我们考虑自己去进行一个实现
my_strcpy()
的函数,设置形参为两个字符指针,用于接收主函数传入进来的两个字符串的起始地址void my_strcpy(char* dst, char* src)
my_strcpy(str1, str2);
src
和dst
两个指针所指向的字符,然后进行一一拷贝,直到*src == '\0’
为止*dst = *src;
*src == '\0'
的时候,便结束拷贝,跳出循环。此时我们还有最后一个'\0'
还没有拷贝过去,继续执行一次*dst = *src
即可代码展示
void my_strcpy(char* dst, char* src)
{
while (*src != '\0')
{
*dst = *src;
src++;
dst++;
}
*dst = *src;
}
运行结果展示
看完了上面这段代码,你认为就结束了吗?其实对于这种代码来说是不够简练的,我们来继续进行一个优化
'\0'
来说就相当于与【假】,所以当*src != '\0'
的时候就会一直循环,就为【真】。所以我们可以直接改成*src
,当其碰到'\0'
的时候就会跳出循环停止拷贝while (*src)
后置++
来说是先执行++之前的,所以赋值完成之后再++就刚好可以达到一个后移的效果*dst++ = *src++;
来看一下代码的优化后的逻辑,其实它还可以再进行一个优化
while (*src)
{
*dst++ = *src++;
}
*dst = *src;
'\0'
,因为在循环中拷贝完之后while()循环中就是那个'\0'
,会自动跳出循环,此时【src】和【dst】也已经遍历结束while (*dst++ = *src++)
{
;
}
运行结果展示
经过上面的众多优化,你一定觉得可以了,确实已经是够简洁了,但是呢却缺乏安全性
char* str2 = NULL;
[空指针异常]
,因为在函数内部现在要执行*src
,也就是解引用的操作,我们知道对于空指针来说是不能解引用的,因此这里就出现问题了,表示我们的程序考虑地不够严谨assert(src != NULL);
src != NULL
时,便不会执行这个断言,只有当src传入进来是NULL的时候才会触发这个断言assert(src);
只有里面的表达式expression为真的时候才会执行,为假的时候便不会执行assert(dst);
那么这两个断言的逻辑就可以转换为只有当src
和dst
均为非空的时候程序才正常执行,只要有一方为空便报出错误,那便将它们做一个合并,就可以想到使用我们在操作符章节讲到过的【逻辑与】
assert(dst && src);
看完了上面的这些,那你一定会觉得这个这个代码非常严谨了吧,但是不要高兴得太早,还有问题
假设一个公司的程序员,它现在就在模拟实现一个字符串strcpy(),也想到了断言这一步,然后吃饭去了。和朋友一起到楼下酒吧喝了两杯,然后呢回到公司之后继续写业务,要知道此时他喝醉了
while (*src++ = *dst++)
{
;
}
dst
中的内容拷贝到了原字符串src
中,此时虽然在拷贝的过程中不会出现什么问题,可是呢在运行的时候就会出现【变量str周围的堆栈已损坏】,也就是【str1】中的这些“xxxxxxxxx”若是拷贝到str2中是存不下的,这就出现问题了src
,那我们要将原字符串拷贝到目标字符串中,原字符串肯定不能修改,所以这个时候就要使用到【const常】了。此时我们可以在char* src
的前面加上一个const作为修饰,此时若是这个喝醉酒的程序员把拷贝的字符串反了,编译时期就会直接报出错误src
来说就叫做【常量指针】,它所指向的内容是不可以修改的,但是它的指向是可以修改的,若是不太清楚可以看看这篇文章常量指针与指针常量可能有同学说,就这么一个小小的const也这么讲究,那我要和你说:我们写业务逻辑就是要严谨,你永远不可能知道用户下一秒会做什么。加上了const之后使得我们的代码更具有健壮性防止源头被修改,也就可以扼杀一个运行时错误❌
最后的话再进行一个完善也就是我们前面说到过的有关这个strcpy()函数还具有一个返回值,也就是
char*
,返回的是【dst】拷贝后的内容
char* ret = src;
return ret;
char* my_strcpy(char* dst, const char* src)
{
assert(dst && src);
char* ret = src;
while (*dst++ = *src++)
{
;
}
return ret;
}
int main(void)
{
char str1[10] = "xxxxxxxxx";
char str2[] = "hello";
printf("str1 = %s\n", my_strcpy(str1, str2));
return 0;
}
到这里,我么的模拟实现就算是真正完成了,相信在跟着我一步步地这么思考下来,一点点地做修改,完成代码。回顾整个流程。相信你的逻辑思维一定得到了提升,更加严密
为何以梅开n度作为标题,一方面除了【吸睛】之外,其实也在反映我们的程序人生
20%
在写业务逻辑,但是80%
在调BUG,修BUG,但其实这都是你的代码问题导致的,若是我们在第一次写代码的时候就将问题考虑得很仔细、很周全,其实是可以减轻很多负担的2023年2月18日记,有感而发,还望采纳❤