模拟实现Strcpy函数 – 通过函数发现 “程序之美” | 不断优化、优化、再优化~


前言

通过对C语言中 Strcpy 库函数的自我实现,探索程序的 优化之美 ,探索程序的 奥秘!


一、Strcpy库函数是什么?

在这里插入图片描述
Strcpy库函数作用:实现字符串的复制

strcpy ( char * destination, const char * source );

实现原理: 将想要复制的字符串地址放在strcpy函数括号的后面,而将复制的字符串的目的地放在括号前面,它们通过传址的方式,实现字符串的复制

通过上面简单的了解,不妨发现, 复制字符串实则可以通过一个一个的字符交换实现复制那我们现在就可以实践实现自定义函数实现Strcpy函数

二、Strcpy的用法

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	
	strcpy(arr1,arr2); //1.目标空间的起始地址, 2.原空间的起始地址
	
	printf("%sn", arr1);
	
	return 0;
}

请添加图片描述
这里打印的是已经复制过去的字符串

可能有同学要问:“那 arr1数组 里 剩下的 x呢?”

在这里可以观察到:Strcpy 是将 arr2 里的 “hello” 复制到 arr1 中
( 而且是从首元素一个一个覆盖的复制过去 )

本质:是将 arr2 里的 “h e l l o ” 去覆盖 arr1 里前几个 “x”

也就是说:打印 已完成复制的 arr1 时printf 会打印到 “” 前的所有字符("" 为字符串的结束标识符) ,所以会打印到 “” 就截止打印

这也就是为什么不会打印后面剩下的“x”了

当我们解决完这个小插曲后,也就基本了解到 Strcpy 的工作原理了
接下来就让我们用自己的语言去实现它吧!

三、My_Strcpy的实现

1.“前菜”

先准备好实现 My_Strcpy 的程序吧

#include <stdio.h>
#include <string.h>
int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	
	My_Strcpy(arr1,arr2); //1.目标空间的起始地址, 2.原空间的起始地址
	
	printf("%sn", arr1);
	
	return 0;
}

2.实现 My_Strcpy 函数

Ⅰ.“复制过程”的优化

此处忽略“前菜”
直接开始 自定义函数部分

void My_strlen(char* dest,char* src) //dest指针接收目标数组的首元素地址;
                                     //src指针接收要复制的数组首元素地址
{
  while(*src != '')
  {
	*dest = *src;
	dest++;
	src++;
  } //但是这样设置循环条件的话,是不会把''也给复制的
	//所以得加多一条语句
	*dest = *src; //因为上面循环已经让地址到''的地方
	              //只是循环条件为假,所以不执行
	              //所以可以承接上面++后的地址,执行一次,即可把''也复制
}

但是有没有什么办法将’’也整合在循环里呢?

如果你也有这样的想法
那恭喜你
开始步入“程序之美”
优化

那接下来,让我们开始 “优化” 吧!!

为了可以“一步到位”,我们可以尝试把 循环内的条件先整合起来

void My_strlen(char* dest,char* src) 
{
  while(*src != '')
  {
	*dest++ = *src++; //跟之前意思一样
	                  //先复制,然后地址++,然后解引用 复制
  } 
	*dest++ = *src++; 
}

开始“一步到位”吧

void My_strlen(char* dest,char* src) 
{
  while(*dest++ = *src++)
  { 
	;  //上面的条件,既可以先复制
  }   //当复制''后,条件判断为假,又可以退出循环
}   //这样不就能做到“一步到位”、“一石二鸟”了吗!

看到这里
是不是可以感受到 “程序” 所带给人“醍醐灌顶”的感觉了呢?
接下来
还有更 “妙” 的呢!!


Ⅱ.“函数内部”的优化

为了保证函数的可实行性
我们要判断传过来的是否为空指针
这就是 “优化”第二个点啦~

此判断操作,我们可以用 “assert()” – 断言 这个库函数
【这里不细致展开】

那我们就可以用此来判断传过来的两个指针是否为空指针
从而确保函数的可执行性正确性~

void my_strcpy(char*dest , const char*src)  
{
	//判断传过来的 是否为空指针
	assert( src != NULL);//断言----需要引头文件 <assert.h>
    // 如果 != 则 为真--则不会报错,
	//如果为假(即 指针为 空指针)--则会报错                                                             
	assert( dest != NULL);
	while (*dest++ = *src++)
	{
		;
	}
}

加上 assert 还有一个好处就是:如果为空指针,程序也可以快速给出出错的位置 (这就是 if 语句做不到的)

就像下面一样:

在这里插入图片描述

这样的程序用起来谁不放心呢~


Ⅲ.“函数形参”的优化

通过回看前面 “Strcpy库函数是什么” 的章节时
我们不难发现
我们的 My_Strcpy 函数Strcpy库函数 对比起来
还是有 “不同的”
说明我们还有 “优化” 的空间哦!

在这里插入图片描述
不难发现
对比我们的 My_Strcpy 函数形参部分
这里的 “形参” 部分多了 “const” 来修饰

我们此刻就要开始思考–为什么要这样做呢
【PS:要做到这种 勤加思考 的习惯,才是一个优秀的程序标配哦~】

这样做可以保证 在复制的过程中,保护“复制的内容”不会在复制的 过程中而被改变

【PS:这里暂时不细谈“const操作符在指针中的应用”!】
如果大家觉得这篇文章写得不错、觉得通俗易懂
可以多多点赞、转发、收藏哦!
本作者会尽快出这方面相关的内容哦!!

回归正题

在 * 前 加上 ‘const’
表示 * source (对应回 My_Strcpy 中的 ’ * src ’ ) 即 * source这个指针 解引用后所对应的内容 具有 常属性,在复制的过程中, source的值不会被改变*

只有当 source的地址发生改变,即 source所对应的内容也发生改变时,它的值才会发生改变

这也就可以有效避免如下的问题

void My_strlen(char* dest,const char* src) ![在这里插入图片描述](https://img-blog.csdnimg.cn/66a1220bbfbe4150bb78364a367522ee.png#pic_center)

{
  while( *src++ = *dest++)
  { 
	;   //复制的时候,放错位置的情况
  }   
}

发生上述问题时,程序就会自动报错,保护了程序
在这里插入图片描述

在这里插入图片描述
因为此时 *src 具有常属性
相当于赋值的时候,常量不能放在等号的左边一个道理

把src指向的内容拷贝放进dest 指向的空间中
本质上讲,就是希望dest指向的内容被修改,src指向的内容不被修改

这也就是我们所”优化”的另一个点啦~


Ⅳ.“函数返回类型”的优化

再次对照 Strcpy库函数
我们又不难发现,Strcpy库函数返回类型竟然是 char*
这不
“优化” 又来了~~

在这里插入图片描述

Strcpy这个库函数,其实返回的是目标空间起始地址

那我们就可以做如下 “优化”

#include <stdio.h>
#include <string.h>

char* my_strcpy(char*dest , const char*src)  
{

	char * ret = dest; //先保存起始地址下来
	
	assert(*src != NULL)                                                           
	assert(*dest != NULL);
	while (*dest++ = *src++)
	{
		;
	}
	
	return ret; //内容改变了,但地址没变--也就依旧可以使用了

}

int main()
{
	char arr1[20] = "xxxxxxxxxx";
	char arr2[] = "hello";
	
	printf("%sn", My_Strcpy(arr1,arr2)); //这里就可以 “链式访问”
	                            //即【函数的返回值 为 printf的参数】
	                        //这样就可以很快的查看 目标空间内容是什么
	return 0;
}

使用 char * 返回类型,可以更快地查看 目标空间内容是什么啦~~


四、完成 My_Strcpy函数

综上,
我们就成功的将 My_Strcpy函数 ,所有可以优化的点都有优化好啦~
换句话说:我们成功复刻了 Strcpy函数
是不是顿时感觉自己棒棒哒!!

但这只是**“小巫见大巫”**,起到为各位同学 “抛砖引玉” 的作用,希望同学们也可以 借此文章对个人思想有所启发哦!

程序世界之大,
还有更多未知的等待你们
去探索,
去发现,
去找到属于你们豁然开朗的那一刻哦~


五、感谢大家支持!!!

【如果你觉得本文对你有帮助,可以点点赞 支持一下哟~】
【如果你有更好的想法,也可以在下方评论哟,相互学习,相互进步!】

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>