草庐IT

C语言--模拟实现库函数strcpy

偷吃橙子的喵 2023-05-14 原文

目录

前言

本章内容我们将通过相关函数来实现库函数中的strcpy。

strcpy实现的基本原理

C语言 strcpy()函数用于对字符串进行复制(拷贝)。需要的头文件为 <string.h>。原理如下

char* strcpy(char* strDestination, const char* strSource);

其中的strSource为源字符串,strDestination为目的字符串,strcpy的作用就是将 strSource 指向的字符串复制到 strDestination。

我们举个例子

int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxx";
	char arr2[] = "hello";
	strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

打印结果如图1

我们知道字符串arr2实际上是"hello\0",那么我们在打印字符串的时候有没有打印这个\0呢,我们用监视的方法来看一下,如图2

我们发现实际上\0也是被拷贝过来了的。

函数的模拟实现

我们来写一个函数my_strcpy实现字符串的拷贝,框架如下

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

}
int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

当’\0’也被拷贝过去时函数停止运行,我们可以用一个循环来实现代码

void my_strcpy(char* dest, char* src)
{
	while (*src != '\0')
	{
		*dest = *src;
		dest++;
		src++;
	}//当src解引用为'\0'时跳出循环,但'\0'还并没有被复制过去
	*dest = *src;//将最后的'\0'也复制过去
}
int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1, arr2);
	printf("%s\n", arr1);
	return 0;
}

代码优化

上述函数只是我们按照基本思路一步步写出来的,肯定不是最优解,看下面这段代码

void my_strcpy(char* dest, char* src)
{
	while (*dest++=*src++)
	{
		;
	}
}

这段代码才是大师级别的书写格式,它将我们上述的个步骤融为一体,下面我们来分析这段代码。
首先我们将解引用的src的值赋给dest,然后后置++,各自都跳到下一个位置再进行赋值,当最后一次src解引用为’\0’并赋值给dest时,由于’\0’的ascll码值为0,当0被赋值给dest,判断结果也为0,0为假,就会跳出循环,这样既把\0复制了过去,还借此跳出了循环,可谓是一举两得。

assert–断言

假设arr1或arr2为空指针会发生什么呢?显然,空指针是不能解引用的,程序会报错。可是这种情况也是难以避免的,为了避免程序崩溃,我们在函数开头添加一个assert(断言),通过一个判断来选择继续执行或中止代码。

void my_strcpy(char* dest, char* src)
{
	assert(src != NULL);//若为真则继续执行,若为假则终止代码
	while (*dest++=*src++)
	{
		;
	}
}
int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1,NULL);
	printf("%s\n", arr1);
	return 0;
}

我们假定arr2为空指针,代码运行结果就会如下

这样,我们既可以有效防止程序崩溃,还可以很清晰的在代码中锁定出问题的地方(比如途中出问题的地方就为源.c line27)。

const关键字

如果我们在函数书写中把拷贝对象和被拷贝对象写反了,会发生什么呢?

void my_strcpy(char* dest, char* src)
{
	assert(src != NULL);
	while (*src++=*dest++)
	{
		;
	}
}
int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxx";
	char arr2[] = "hello";
	my_strcpy(arr1,arr2);
	printf("%s\n", arr1);
	return 0;
}

代码运行结果如图5

我们会发现程序崩溃了,原因在于arr2字符串长度放不下arr1。但如果我们加入const关键字修饰就能很好的规避这个问题。

const关键字的作用在于修饰变量,这个变量就被称为常变量,它具备了常量的属性,但本质上还是一个变量。
但我们来看一段代码

int main()
{
    const int num = 10;
	int* p = &num;
	*p = 20;

	printf("%d\n",num);
}

运行结果如图6

我们发现,被const修饰的变量num还是被修改了,我么要想保持num不变,就需要修饰指针变量

int main()
{
    int num = 10;
	const int* p = &num;
	*p = 20;

	printf("%d\n",num);
}

这样* p=20这段代码将无法实行。这是因为const如果放在* 左边,修饰的是*p,表示的是指针指向的内容,是不能通过指针来改变的,但是指针变量本身( p )是可以修改的,如果放在 *右边,修饰的就是p,和放在左边完全相反,指针变量本身将无法被修改,但指针指向的内容却可以被修改,num还是会被修改为20

strcpy的返回值

strcpy这个库函数的执行原理实际上是会返回目标空间的起始地址

char* my_strcpy(char* dest, const char* src)
{
	assert(src != NULL);
	assert(dest != NULL);
	char* ret = dest;
	while (*dest++=*src++)
	{
		;
	}
	return ret;
}
int main()
{
	char arr1[20] = "xxxxxxxxxxxxxxx";
	char arr2[] = "hello";
	//目标空间的起始地址 源空间的起始地址
	my_strcpy(arr1,arr2);
	printf("%s\n", my_strcpy(arr1,arr2);//链式访问
	return 0;
}

结语

以上就是标准的库函数strcpy的模拟实现流程,如有出入,欢迎指正。

有关C语言--模拟实现库函数strcpy的更多相关文章

  1. ruby - 如何将脚本文件的末尾读取为数据文件(Perl 或任何其他语言) - 2

    我正在寻找执行以下操作的正确语法(在Perl、Shell或Ruby中):#variabletoaccessthedatalinesappendedasafileEND_OF_SCRIPT_MARKERrawdatastartshereanditcontinues. 最佳答案 Perl用__DATA__做这个:#!/usr/bin/perlusestrict;usewarnings;while(){print;}__DATA__Texttoprintgoeshere 关于ruby-如何将脚

  2. ruby - 如何模拟 Net::HTTP::Post? - 2

    是的,我知道最好使用webmock,但我想知道如何在RSpec中模拟此方法:defmethod_to_testurl=URI.parseurireq=Net::HTTP::Post.newurl.pathres=Net::HTTP.start(url.host,url.port)do|http|http.requestreq,foo:1endresend这是RSpec:let(:uri){'http://example.com'}specify'HTTPcall'dohttp=mock:httpNet::HTTP.stub!(:start).and_yieldhttphttp.shou

  3. ruby - 在没有 sass 引擎的情况下使用 sass 颜色函数 - 2

    我想在一个没有Sass引擎的类中使用Sass颜色函数。我已经在项目中使用了sassgem,所以我认为搭载会像以下一样简单:classRectangleincludeSass::Script::FunctionsdefcolorSass::Script::Color.new([0x82,0x39,0x06])enddefrender#hamlengineexecutedwithcontextofself#sothatwithintemlateicouldcall#%stop{offset:'0%',stop:{color:lighten(color)}}endend更新:参见上面的#re

  4. ruby-on-rails - 在 ruby​​ 中使用 gsub 函数替换单词 - 2

    我正在尝试用ruby​​中的gsub函数替换字符串中的某些单词,但有时效果很好,在某些情况下会出现此错误?这种格式有什么问题吗NoMethodError(undefinedmethod`gsub!'fornil:NilClass):模型.rbclassTest"replacethisID1",WAY=>"replacethisID2andID3",DELTA=>"replacethisID4"}end另一个模型.rbclassCheck 最佳答案 啊,我找到了!gsub!是一个非常奇怪的方法。首先,它替换了字符串,所以它实际上修改了

  5. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  6. ruby - 在 Ruby 中有条件地定义函数 - 2

    我有一些代码在几个不同的位置之一运行:作为具有调试输出的命令行工具,作为不接受任何输出的更大程序的一部分,以及在Rails环境中。有时我需要根据代码的位置对代码进行细微的更改,我意识到以下样式似乎可行:print"Testingnestedfunctionsdefined\n"CLI=trueifCLIdeftest_printprint"CommandLineVersion\n"endelsedeftest_printprint"ReleaseVersion\n"endendtest_print()这导致:TestingnestedfunctionsdefinedCommandLin

  7. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  8. ruby - 在 Ruby 中按名称传递函数 - 2

    如何在Ruby中按名称传递函数?(我使用Ruby才几个小时,所以我还在想办法。)nums=[1,2,3,4]#Thisworks,butismoreverbosethanI'dlikenums.eachdo|i|putsiend#InJS,Icouldjustdosomethinglike:#nums.forEach(console.log)#InF#,itwouldbesomethinglike:#List.iternums(printf"%A")#InRuby,IwishIcoulddosomethinglike:nums.eachputs在Ruby中能不能做到类似的简洁?我可以只

  9. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  10. Unity 热更新技术 | (三) Lua语言基本介绍及下载安装 - 2

    ?博客主页:https://xiaoy.blog.csdn.net?本文由呆呆敲代码的小Y原创,首发于CSDN??学习专栏推荐:Unity系统学习专栏?游戏制作专栏推荐:游戏制作?Unity实战100例专栏推荐:Unity实战100例教程?欢迎点赞?收藏⭐留言?如有错误敬请指正!?未来很长,值得我们全力奔赴更美好的生活✨------------------❤️分割线❤️-------------------------

随机推荐