模拟实现库函数memcpy--复制内存块。详细理解内存重叠及精准复制问题

一.对库函数memcpy的了解

通过在MSDN或者cplusplus网站上检索memcpy
在这里插入图片描述
通过对memcpy的检索,可以初步了解到memcpy以下信息:
1.有三个参数,目标指针,源头指针均为泛型指针 void* () (具有通用性,且不能直接被解引用)
2.该函数不检查源中是否有任何终止空字符 , 它始终精确地复制数字字节
3.为避免溢出,目标参数和源参数所指向的数组的大小应至少为 num 个字节
4.目标数据和源数据不能重叠

二.模拟实现库函数memcpy

思路分析:

1.通过对库函数memcpy的了解,它的前面两个参数都是void类型的泛型指针,无法直接解引用,并且用户使用时候可能传入的是int 、char、float等类型中的一种,这对传入后复制多少个字节的内容带来了一定难度,因此这个void的参数应该转换为什么类型是我们应该首要考虑的

假设如下图,将参数强转为int类型进行操作,那么arr+1将跳过四个字节,如果传入的size_t num=6,那么将对第二个字节的内容读取前面两个字节的内容,进行解引用以后会产生影响,因此,在这应当选择操作力度最小的char类型进行操作,一次跳过一个字节,就能很好的规避上面的问题
在这里插入图片描述
2.循环num次,将源头数据的内容覆盖到目标数组中

代码如下:

#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* str1, const void* str2, size_t num)
{
    
    
	assert(str1 && str2);
	void* p = str1;//返回目标地址,记录目标地址起始位置
	while (num--)
	{
    
    
		*(char*)str1 = *(char*)str2;
		str1 =(char*)str1+1;
		str2 =(char*)str2+1;

	}
	return p;
}
int main()
{
    
    
	char arr1[] = "abcdef";
	char arr2[20] = "hh";
	my_memcpy(arr2, arr1, 3);
	printf("%s\n", arr2);
	return 0;
}

运行结果如下:
在这里插入图片描述

三.对库函数memcpy的具体说明

1.如果需要复制int类型的数据该如何

若为其他类型,在传参时,传入的字节数尤为重要,只需要将你想要的复制的元素个数乘上类型的大小即可

#include <stdio.h>
#include <assert.h>
void* my_memcpy(void* str1, const void* str2, size_t num)
{
    
    
	assert(str1 && str2);
	void* p = str1;//返回目标地址,记录目标地址起始位置
	while (num--)
	{
    
    
		*(char*)str1 = *(char*)str2;
		str1 =(char*)str1+1;
		str2 =(char*)str2+1;

	}
	return p;
}
int main()
{
    
    
	int arr1[] = {
    
    1234};
	int arr2[20] = {
    
    0};
	int sz=sizeof(arr1)/sizeof(arr1[0]);
	my_memcpy(arr2, arr1, 3*sizeof(int));
	for(i=0;i<sz;i++{
    
    
	   printf("%d ",arr2[i]);
	}
	
	return 0;
}

运行结果如下:
在这里插入图片描述

2.它始终精确地复制数字字节

对于开头提到的该函数不检查源中是否有任何终止空字符 ,它始终精确地复制数字字节。这句话表明无论源头数据是否具有\0,它会连同\0一同复制到目标数据中。

代码如下:

int main()
{
    
    
	char arr1[] = "a\0bcdef";
	char arr2[20] = "hh";
	my_memcpy(arr2, arr1, 3);
	printf("%s\n", arr2);
	return 0;
}

运行结果如下:
在这里插入图片描述
可以看到,原本应当打印a\0b,由于打印的时字符串,因此只打印了a,通过观察内存可以验证该结果

调试观察内存可以看到:
在这里插入图片描述
因此,库函数memcpy会将num个字节的内容复制到目标空间中,终止符也可能会被复制。

3.覆盖目标空间的内容

通过(2)上面代码的运行结果可以看到,arr2中原本有“hh”这个字符串,但是最后结果内存中却只有a\0b无论arr2这个源头空间中原本是否有数据,memcpy都会将其覆盖。

4.目标数据和源数据不能重叠(重点理解)

当出现下面这种情况时,数据会出现丢失
例如:

int main()
{
    
    
	char arr1[] = "abcdef";
	char arr2[20] = "hh";
	my_memcpy(arr1+2, arr1, 4);
	//         str1    str2
	printf("%s\n", arr1);
	return 0;
}

在这里插入图片描述当a覆盖到c的位置时,c已经被覆盖成了a,st2指向原本c的位置时,拷贝给st1的内容就是a了,不是原本的c造成了数据的丢失:
在这里插入图片描述
运行结果如下:
在这里插入图片描述
因此,目标数据与源头数据的内存不能重叠,否则就会发生数据拷贝内容被覆盖,数据出现偏差。

5.防止越界

由于库函数memcpy时将源头的num个字节的内容覆盖到目标内存中,因此,目标内存有可能出现越界情况。

当出现下面情况时,会发生越界崩溃
在这里插入图片描述

int main()
{
    
    
	char arr1[] = "abcdef";
	char arr2[20] = "hh";
	my_memcpy(arr1+4, arr1, 4);
	//         str1    str2
	printf("%s\n", arr1);
	return 0;
}

st1向后访问num个字节以后比sizeof(arr1)大的情况下,会发生越界,应当使用时防止该情况发生。
在这里插入图片描述
运行结果如下:
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_68288689/article/details/125857363
今日推荐