【C语言】strcpy函数的超细节详解(什么是strcpy,如何模拟实现strcpy?)

您所在的位置:网站首页 字符串的用处是什么 【C语言】strcpy函数的超细节详解(什么是strcpy,如何模拟实现strcpy?)

【C语言】strcpy函数的超细节详解(什么是strcpy,如何模拟实现strcpy?)

2024-07-08 10:04:42| 来源: 网络整理| 查看: 265

目录

一、观察strcpy()库函数的功能与实现

 二、模仿实现strcpy()函数

 🔍优化代码

 🔍assert断言拦截

 🔍const修饰常量指针

 🔍返回值的加入

三、共勉

一、观察strcpy()库函数的功能与实现

首先我们先来观察一下库函数strcpy去实现字符串拷贝的功能

清楚了这个库函数的功能之后,我们在来详细的解析一下:

 ✨函数头文件

#include

 ✨函数原型详解

char * strcpy ( char * destination, const char * source ); destination :表示目标字符串的地址。 source :表示源字符串的地址。

注意:这里的原字符串地址是被 const 所修饰的常量指针,指向的内容不能被修改

函数的返回值为目标字符串的地址。

✨ 使用strcpy函数的基本步骤:

确保目标字符串destination有足够的空间来存储源字符串src的内容。调用strcpy函数,将源字符串src的内容(包括'\0')复制到目标字符串destination中。返回目标字符串destination的地址。  清楚了这个库函数的功能之后,我们到VS中来看看使用代码如何实现可以看到,首先去定义出两个字符串:第一个str1为目标字符串,初始化均为x是为了在调试的时候方便查看是否拷贝成功;第二个str2为源头字符串 int main(void) { char str1[10] = "xxxxxxxxx"; char str2[] = "hello"; strcpy(str1, str2); printf("拷贝后的结果为: %s\n", str1); return 0; }

然后看到如下就完成了字符串的一个拷贝工作,会连带\0一起拷贝过去。所以对于目标字符串我没有初始化为0就是为了看出拷贝完成的工作

 ✨ 运行结果

 二、模仿实现strcpy()函数

好,看完了库函数的实现之后,我们考虑自己去进行一个实现 

通过定义出一个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* dest, char* scr) { while (*scr != '\0') { *dest = *scr; scr++; dest++; } *dest = *scr; // 将最后的'\0'拷贝过来 }

 运行结果展示

 🔍优化代码

看完了上面这段代码,你认为就结束了吗?其实对于这种代码来说是不够简练的,我们来继续进行一个优化 

对于while循环内部的判断,我们知道是一个逻辑表达式,而对于'\0'来说就相当于与【假】,所以当*src != '\0'的时候就会一直循环,就为【真】。所以我们可以直接改成*src,当其碰到'\0'的时候就会跳出循环停止拷贝 while (*src) 第二处可以优化的就是循环内部的一个拷贝的过程,因为在每一次拷贝完成之后两个字符指针就会进行一个后移,此时我们可以对它们进行一些合并。因为对于后置++来说是先执行++之前的,所以赋值完成之后再++就刚好可以达到一个后移的效果 *dst++ = *src++;

 来看一下代码的优化后的逻辑,其实它还可以再进行一个优化🐉

while (*src) { *dst++ = *src++; } *dst = *src; 通过仔细观察库函数strcpy()的描述后就可以发现,其实它在拷贝结束之后也是存在返回值的,返回的就是拷贝完成之后的目标字符串

因此我们可以将拷贝的逻辑也放到循环的条件判断中去,不需要在最后继续拷贝'\0',随着*dst++ = *src++的不断执行,最后将src中的\0拷贝到了dest中,此时while()循环中的条件就变成了\0,会自动跳出循环,此时【src】和【dst】也已经遍历结束所以代码就被简化成了下面这样👇 while (*dst++ = *src++) { ; }

 运行结果展示

🔍assert断言拦截

 经过上面的众多优化,你一定觉得可以了,确实已经是够简洁了,但是呢却缺乏安全性🛡

我们是模拟实现字符串的拷贝,将str2中的字符串拷贝到str1中,那就是要源头字符串中有内容才可以拷贝,但若是我将这个str置为NULL然后传进去呢,会发生什么? char* str2 = NULL;

通过运行可以看到,运行的时候报出了[空指针异常],因为在函数内部现在要执行*src,也就是解引用的操作,我们知道对于空指针来说是不能解引用的,因此这里就出现问题了,表示我们的程序考虑地不够严谨 此时就可以使用到一样东西叫做【断言】,可以去看看官方文档  👉assert

assert(src != NULL); 若是加上了这句assert断言,那么编译器在运行的时候就会报出对应的错误信息,括号里面要写上的就是出错的对立面,若是当src != NULL时,便不会执行这个断言,只有当src传入进来是NULL的时候才会触发这个断言当然为了方便也可以写成这样👉assert(src);只有里面的表达式expression为真的时候才会执行,为假的时候便不会执行也可以给dest加上断言,防止它传入进来也为NULL,👉assert(dest);

 那么这两个断言的逻辑就可以转换为只有当src和dst均为非空的时候程序才正常执行,只要有一方为空便报出错误,那便将它们做一个合并,就可以想到使用我们在操作符章节讲到过的【逻辑与】

assert(dst && src);

 🔍const修饰常量指针

 看完了上面的这些,那你一定会觉得这个这个代码非常严谨了吧,但是不要高兴得太早,还有问题😮

 假设一个公司的程序员,它现在就在模拟实现一个字符串strcpy(),也想到了断言这一步,然后吃饭去了。和朋友一起到楼下酒吧喝了两杯,然后呢回到公司之后继续写业务,要知道此时他喝醉了🍺

while (*src++ = *dst++) // 写反了 { ; } 于是呢他就将代码写成了上面这样,将目标字符串dest中的内容拷贝到了原字符串src中,此时虽然在拷贝的过程中不会出现什么问题,可是呢在运行的时候就会出现【变量str周围的堆栈已损坏】,也就是【str1】中的这些“xxxxxxxxx”若是拷贝到str2中是存不下的,这就出现问题了

那么上述的这个程序员的操作其实是在修改源头字符串src,那我们要将原字符串拷贝到目标字符串中,原字符串肯定不能修改,所以这个时候就要使用到【const常】了。此时我们可以在char* src的前面加上一个const作为修饰,此时若是这个喝醉酒的程序员把拷贝的字符串反了,编译时期就会直接报出错误

此时对于src来说就叫做【常量指针】,它所指向的内容是不可以修改的,但是它的指向是可以修改的,若是不太清楚可以看看这篇文章👉常量指针与指针常量

可能有同学说,就这么一个小小的const也这么讲究,那我要和你说:我们写业务逻辑就是要严谨,你永远不可能知道用户下一秒会做什么。加上了const之后使得我们的代码更具有健壮性💪防止源头被修改,也就可以扼杀一个运行错误❌ 

 🔍返回值的加入

最后的话再进行一个完善也就是我们前面说到过的有关这个strcpy()函数还具有一个返回值,也就是char*,返回的是【dest】拷贝后的内容 

因为我们是进行一个模拟,所以为了尽量和原本的内容保持一致,我们也要将这个返回值加上,这个很简单,只需要在一开始的时候保存一下 dest 字符串的首地址  char* ret = dest; 最后将其返回即可 return ret;

 那么官方要加上这个char *的目的是什么呢?从下面的printf语句其实就可以看出是为了实现一个【链式访问】

什么是链式访问呢?也就是将一个函数的返回值作为另一个函数的参数,设想若是这个函数的返回类型是void的话,那么它还能不能放在这里呢 printf("str1 = %s\n", my_strcpy(str1, str2));

 以下便是整体代码展示

char* my_strcpy(char* dest, const char* src) { assert(dest && src); char* ret = dest; while (*dest++ = *src++) { ; } return ret; } int main(void) { char str1[10] = "xxxxxxxxx"; char str2[] = "hello"; printf("str1 = %s\n", my_strcpy(str1, str2)); return 0; }

到这里,我么的模拟实现就算是真正完成了,相信在跟着我一步步地这么思考下来,一点点地做修改,完成代码。回顾整个流程。相信你的逻辑思维一定得到了提升,更加严密💪 

三、共勉

 以下就是我对strcpy函数的超细节详解的理解,如果有不懂和发现问题的小伙伴,请在评论区说出来哦,同时我还会继续更新对C++的理解,请持续关注我哦!!!   



【本文地址】

公司简介

联系我们

今日新闻


点击排行

实验室常用的仪器、试剂和
说到实验室常用到的东西,主要就分为仪器、试剂和耗
不用再找了,全球10大实验
01、赛默飞世尔科技(热电)Thermo Fisher Scientif
三代水柜的量产巅峰T-72坦
作者:寞寒最近,西边闹腾挺大,本来小寞以为忙完这
通风柜跟实验室通风系统有
说到通风柜跟实验室通风,不少人都纠结二者到底是不
集消毒杀菌、烘干收纳为一
厨房是家里细菌较多的地方,潮湿的环境、没有完全密
实验室设备之全钢实验台如
全钢实验台是实验室家具中较为重要的家具之一,很多

推荐新闻


图片新闻

实验室药品柜的特性有哪些
实验室药品柜是实验室家具的重要组成部分之一,主要
小学科学实验中有哪些教学
计算机 计算器 一般 打孔器 打气筒 仪器车 显微镜
实验室各种仪器原理动图讲
1.紫外分光光谱UV分析原理:吸收紫外光能量,引起分
高中化学常见仪器及实验装
1、可加热仪器:2、计量仪器:(1)仪器A的名称:量
微生物操作主要设备和器具
今天盘点一下微生物操作主要设备和器具,别嫌我啰嗦
浅谈通风柜使用基本常识
 众所周知,通风柜功能中最主要的就是排气功能。在

专题文章

    CopyRight 2018-2019 实验室设备网 版权所有 win10的实时保护怎么永久关闭