C语言strcpy和memcpy

先用代码给大家简单的模拟下strcpy和memcpy的“原理”。为了方便对指针不熟悉的人阅读,下面都改成了用数组方式表示。

  • char* strcpy(char* dest, const char* src);把含有’\0’结束符的字符串复制到另一个地址空间,返回值的类型为char*。
char* myStrcpy(char* dest, const char* src)
{
	assert((dest != NULL) && (src != NULL));
	int i = 0;
	while ((dest[i] = src[i++]) != '\0');
	return dest;
}

//char* myStrcpy(char* dest, const char* src)
//{
//	assert((dest != NULL) && (src != NULL));
//	int i = 0;
//	while ((*dest++ = *src++) != '\0');
//	return dest;
//}

我们都知道字符串是以’\0’结尾的,其实就是数值0,用’\0’表示的更明确。所以字符串拷贝是遇到’\0’就结束了,不管你char *(内存空间)有多大,够不够放。缺点:1、有时候就会出现内存空间不够大出现操作非法地址的问题,所以就出现了strcpy_s的相对安全的函数,这比strcpy多了个参数,就是确保即使没有遇到’\0’,也能在达到最大拷贝个数时结束拷贝。2、只适用于字符串拷贝,例如变量拷贝、数据拷贝就不适合了。
如果有人看不懂while ((dest[i] = src[i++]) != ‘\0’);文章末尾有对这进行反汇编用C语言的方式去分析他的执行过程。

  • void memcpy(void destin, const void* source, unsigned int n);函数的功能是从源内存地址的起始位置开始拷贝若干个字节到目标内存地址中,即从源source中拷贝n个字节到目标destin中。
/*
* n代表的是字节数(内存单元个数),不是元素个数或者数组长度。
* 如果不知道数组占用多大的内存空间,那可以用sizeof去求,或者数组长度*sizeof(数组类型)
*/
void *myMemcpy(void* destin, const  void* source, unsigned int n)
{
	if ((destin == NULL) || (source == NULL))
		return NULL;
	char* in = (char *)destin;
	char* src = (char *)source;
	while (n--)
		in[n] = src[n];
	return in;
}
  • main()
int main()
{
	char str[10] = "0124";
	char strcpyStr[10] = {0};
	char memcpyStr[10];
	myStrcpy(&str[6], "abc");
	myStrcpy(strcpyStr, str);
	myMemcpy(memcpyStr, str, sizeof(str));
	return 1;
}

调完第一个myStrcpy后,str的数据如下截图,从下标6开始拷贝了"abc"。
在这里插入图片描述
调完第二个myStrcpy后,strcpyStr的数据如下截图,只拷贝了str的前5个内容,因为第5个内容是’\0’,所以结束了。
在这里插入图片描述
调完myMemcpy之后,memcpyStr的数据如下截图,str的10个内容。
在这里插入图片描述
结论:strcpy适用于字符串拷贝,而memcpy适用于任何内存拷贝,包括字符串拷贝,只不过要先求字符串长度+1,相对strcpy麻烦些。

题外话

  • 反汇编分析while ((dest[i] = src[i++]) != ‘\0’);
地址汇编指令操作数C语言
extern unsigned long eax;
函数参数:char **dest, const char **src;
临时变量int *i = malloc(sizeof(int)); i = 0;
01204130hmoveax,dword ptr [dest]==eax = *dest;
01204133haddeax,dword ptr [i]==eax = eax + *i;
extern unsigned long ecx;
01204136hmovecx,dword ptr [src]==ecx = *src;
01204139haddecx,dword ptr [i]==ecx = ecx + *i;
extern unsigned short dl;
0120413Chmovdl,byte ptr [ecx]==dl = *ecx;
0120413Ehmovbyte ptr [eax],dl==*eax = dl;
01204140hmoveax,dword ptr [dest]==eax = *dest;
01204143haddeax,dword ptr [i]==eax = eax + *i;
01204146hmovsxecx,byte ptr [eax]==ecx = *eax;
extern unsigned long ebp;
unsigned long *stack1 = ebp-0D0h;
01204149hmovdword ptr [ebp-0D0h],ecx==*stack1 = ecx
0120414Fhmovedx,dword ptr [i]==edx = *i;
01204152haddedx,1==edx = edx+1;
01204155movdword ptr [i],edx==*i = edx;
bool ZF;
01204158cmpdword ptr [ebp-0D0h],0==ZF = !(*stack1 - 0);
0120415FjemyStrcpy+9Dh (0120416Dh)==if(ZF == true) goto 0120416Dh;
unsigned long stack2 = ebp-0D4h;
01204161movdword ptr [ebp-0D4h],1==stack2 = 1;
0120416BjmpmyStrcpy+0A7h (01204177h)==goto 01204177h;
0120416Dmovdword ptr [ebp-0D4h],0==stack2 = 0;
01204177cmpdword ptr [ebp-0D4h],0==ZF = !(stack2 - 0);
0120417EjemyStrcpy+0B2h (01204182h)==if(ZF == true) goto 01204182h;
01204180jmpmyStrcpy+60h (01204130h)==goto 01204130h;

eax,ecx,edx,dl,ebp是CPU的寄存器,你可当成是一个全局的unsigned long变量去理解。ZF是标志寄存器上的一位,为1时代表上一步运算结果是0。
从上面的表格可以清楚的知道,dest[i] = src[i++] != ‘\0’是先把src的值取到dest里面,然后再把dest的值取到ecx里,放到* stack1(ebp-0D0h内存地址)上做备用,然后对i的值++,用*stack1去与’\0’比较,如果是0就跳出循环。实际上等同下面的代码

char c;
do{
	dest[i] = src[i];
	c = dest[i];
	++i;
}while(c != '\0');

注:任何的运算都是在寄存器上完成再写回内存地址上的,不能直接在内存地址上做加减法等。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值