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

目录

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

 二、模仿实现strcpy()函数

 🔍优化代码

 🔍assert断言拦截

 🔍const修饰常量指针

 🔍返回值的加入

三、共勉


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

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

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

 ✨函数头文件

#include <string.h>

 ✨函数原型详解

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

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

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

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

  1. 确保目标字符串destination有足够的空间来存储源字符串src的内容。
  2. 调用strcpy函数,将源字符串src的内容(包括'\0')复制到目标字符串destination中。
  3. 返回目标字符串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);
  • 写代码前我们来看一下字符串拷贝的原理,也就是获取到srcdst两个指针所指向的字符,然后进行一一拷贝,直到*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);

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

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++的理解,请持续关注我哦!!!   

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/450756.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

forward请求转发、include请求转发

forward请求转发&#xff0c;执行到这里就请求转发去执行Servlet4 include请求转发在执行完Servlet4之后&#xff0c;还会回来执行完。

五大模型大比拼:Claude3、Gemini、Sora、GPTs与GPT-4的优缺点分析

课程安排 学习内容 第一章 2024年AI领域最新技术 1.OpenAI新模型-GPT-5 2.谷歌新模型-Gemini Ultra 3.Meta新模型-LLama3 4.科大讯飞-星火认知 5.百度-文心一言 6.MoonshotAI-Kimi 7.智谱AI-GLM-4 第二章 OpenAI开发者大会后GPT最新技术 1.最新大模型GPT-4 Turbo详细介绍…

鞋服品牌如何计算门店盈亏平衡?

在鞋服品牌的运营中&#xff0c;门店盈亏平衡是衡量门店经营效果的重要指标。盈亏平衡点意味着门店在达到这一销售水平时&#xff0c;既能够覆盖所有固定和变动成本&#xff0c;又能实现零利润或零亏损。计算门店盈亏平衡有助于品牌更好地理解门店的经营状况&#xff0c;制定合…

解决LangChain构建知识向量库的过程中官方API无法自定义文本切割方式的问题-例如按行切分

自定义切分构成知识向量库的文本o(&#xffe3;▽&#xffe3;)ブ 在使用大模型和知识向量库进行问题问答的过程中&#xff0c;由于一些LangChain切分文本功能上的限制影响了模型的回答效果为了解决该问题故诞生此文档&#xff0c;如果有说的不对的&#xff0c;或者想交流的非…

Vue3中computed、watch、watchEffect的区别

三者都是侦听工具&#xff0c;实现的是观察者模式&#xff0c;横向对比 &#xff08;1&#xff09;依赖&#xff1a;指的是响应性依赖&#xff0c;也就是侦听 ref、reactive 这类具有响应性的对象。 &#xff08;2&#xff09;watch&#xff1a;默认情况下&#xff0c;被侦听对…

Spring AOP常见面试题

目录 一、对于AOP的理解 二、Spring是如何实现AOP的 1、execution表达式 2、annotation 3、基于Spring API&#xff0c;通过xml配置的方式。 4、基于代理实现 三、Spring AOP的实现原理 四、Spring是如何选择使用哪种动态代理 1、Spring Framework 2、Spring Boot 五…

CUDA入门之统一内存

原文来自CUDA 编程入门之统一内存 &#x1f3ac;个人简介&#xff1a;一个全栈工程师的升级之路&#xff01; &#x1f4cb;个人专栏&#xff1a;高性能&#xff08;HPC&#xff09;开发基础教程 &#x1f380;CSDN主页 发狂的小花 &#x1f304;人生秘诀&#xff1a;学习的本质…

X64 页表结构

PML4&#xff08;Page Map Level 4&#xff09;是x86-64架构中用于管理虚拟内存地址翻译的四级页表结构之一。它是一种树形结构&#xff0c;由多个页目录表&#xff08;Page Directory Pointer Table&#xff0c;PDPT&#xff09;组成&#xff0c;每个PDPT有512个指向下一级页表…

低功耗DC-DC电压调整器IU5528D

IU5528D是一款超微小型,超低功耗,高效率,升降压一体DC-DC调整器。适用于双节,三节干电池或者单节锂电池的应用场景。可以有效的延长电池的使用时间。IU5528D由电流模PWM控制环路&#xff0c;误差放大器&#xff0c;比较器和功率开关等模块组成。该芯片可在较宽负载范围内高效稳…

1_springboot_shiro_jwt_多端认证鉴权_Shiro入门

1. Shiro简介 Shiro 是 Java 的一个安全框架&#xff0c;它相对比较简单。主要特性&#xff1a; Authentication&#xff08;认证&#xff09;&#xff1a;用户身份识别&#xff0c;通常被称为用户“登录”&#xff0c;即 “你是谁”Authorization&#xff08;授权&#xff…

webpack-dev-server5.0+ 版本问题

webpack-dev-server版本选择 在使用webpack-dev-server搭建新项目时&#xff0c;需要依赖node 和webpack以及webpack-cli 这是需要注意各个应用之间的版本问题 通过npm官网查看webpack-dev-server使用的版本依赖对象 先看package.json&#xff0c;可以看到当前的版本 再找到依…

YOLOv9使用训练好的权重检测目标

打开yolov9-main\detect.py文件 1修改为训练后权重文件的位置 2改为要检测图片的位置 3修改成数据集的yaml文件 运行detect.py文件并解决报错 打开报错文件yolov9-main\utils\general.py,在prediction = prediction[0]后边加上[0] 继续运行detect.py,成功检测 存在问题 当…

9个免费游戏后端平台

在这篇文章中&#xff0c;您将看到 九个免费的游戏服务平台提供商&#xff0c;这可以帮助您开始在线多人游戏&#xff0c;而无需预先投入大量资金。 每个提供商都有非常独特的功能&#xff0c;因此成本应该只是决定时要考虑的方面之一。 我还从低预算项目的角度对免费提供商进…

如何在Ubuntu系统部署DbGate数据库管理工具并结合cpolar内网穿透远程访问

文章目录 1. 安装Docker2. 使用Docker拉取DbGate镜像3. 创建并启动DbGate容器4. 本地连接测试5. 公网远程访问本地DbGate容器5.1 内网穿透工具安装5.2 创建远程连接公网地址5.3 使用固定公网地址远程访问 本文主要介绍如何在Linux Ubuntu系统中使用Docker部署DbGate数据库管理工…

WordPress供求插件API文档:获取市场类型

请注意&#xff0c;该文档为&#xff1a; WordPress供求插件&#xff1a;一款专注于同城生活信息发布的插件-CSDN博客文章浏览阅读396次&#xff0c;点赞6次&#xff0c;收藏5次。WordPress供求插件&#xff1a;sliver-urban-life 是一款专注于提供同城生活信息发布与查看的插件…

CSS伪类与常用标签属性整理与块级、行级、行级块标签(文本,背景,列表,透明,display)

目录 文本 color&#xff1a;字体颜色 font-size&#xff1a;字体大小​编辑 front-family&#xff1a;字体 text-align&#xff1a;文本对齐 text-decoration:line-through&#xff1a;定义穿过文本下的一条线 text-decoration:underline&#xff1a;定义文本下的一条线…

mysqld.exe运行时,提示缺少msvcr100.dll,msvcp100.dll文件,导致mysql安装失败或mysql服务无法启动

mysqld.exe运行时&#xff0c;提示缺少msvcr100.dll&#xff0c;msvcp100.dll文件&#xff0c;导致mysql安装失败或无法启动 msvcr100.dll&#xff0c;msvcp100.dll时VC2010的动态链接库。 1、下载地址 https://www.microsoft.com/zh-cn/download/details.aspx?id26999&wd…

027—pandas 不同分类每天指定取值的比例

前言 本例我们将进行分组计算&#xff0c;分组后得到一个堆叠数据&#xff0c;并对堆叠数据解除堆叠&#xff0c;最后再按要求格式化为百分数样式。 此类操作会经常发生在业务数据透视场景下&#xff0c;一般都会有 Excel 来操作完成&#xff0c;今天我们使用 Python 的 panda…

钉钉扫码登录,sdk问题

别问我为啥会写这玩意。因为有人问到了 1.钉钉扫码登录&#xff0c;网上代码一大堆&#xff0c;但是小同学在抄的时候突然问我&#xff0c;为啥jar包倒入不了。pom添加了&#xff0c;镜像也是阿里的&#xff0c;还是不行 下载了包&#xff0c;按这个放啊发去操作就好了 1.先…

1.Datax数据同步之Windows下,mysql数据同步至另一个mysql数据库

目录 前言步骤操作大纲步骤明细其他问题 前言 Datax是什么&#xff1f; DataX 是阿里巴巴集团内被广泛使用的离线数据同步工具/平台&#xff0c;实现包括 MySQL、SQL Server、Oracle、PostgreSQL、HDFS、Hive、HBase、OTS、ODPS 等各种异构数据源之间高效的数据同步功能。准备…