实现memcpy函数和memmove函数

实现memcpy函数和memmove函数

前言

本文介绍了如何自己实现memcpy函数和memmove函数的一些思路。

memcpy库函数

  • 以下是memcpy函数的文档解释

void *memcpy(void str1, const void str2, size_t n)
参数
str1 – 指向用于存储复制内容的目标数组,类型强制转换为 void
指针。
str2 – 指向要复制的数据源,类型强制转换为 void
指针。
n – 要被复制的字节数。
返回值
该函数返回一个指向目标存储区 str1 的指针。


笔者上一篇写了如何实现strcpy函数,这个函数和strcpy函数十分相识,而strcpy函数只能拷贝字符串,memcpy可以拷贝任意类型。
它传入和返回的指针都是void类型,void类型的指针是不允许进行解引用操作和指针加减运算的。strcpy函数只能拷贝字符串,但是memcpy可以拷贝任意类型。


传入的指针都是void*类型的,我们需要把它们转换成其他类型的指针才能进行操作,这里强转成char*类型,char*类型一次操作一个字节,更好进行条件判断。

代码如下(示例):

void* my_memmove(void* dest, const void* src, size_t sum)
{
    
    
	assert(dest && src);
	void* res = dest;//记录dest初始位置。

		while (sum--) //sum大于0就进行拷贝。
		{
    
    
			*(char*)dest = *(char*)src; //一次拷贝一个字节数据。

			++(char*)dest;//每次填过一个字节的空间。
			++(char*)src;
		}

	return res;//返回初始指针。
}


memmove函数

memmove函数和memcpy函数的定义一样。

C语言标准规定memcpy函数只需实现拷贝功能,无需考虑内存重叠问题。

memmove函数则必须解决内存重叠问题,对数据进行正确拷贝,可以看做是memcpy函数的升级版。

但是在vs2013或者vs2019环境下memcpy也实现了memmove功能。


内存重叠是什么意思呢?

在这里插入图片描述

如图,假设我们使用memcpy函数把dest指针指向的内容拷贝到src指向的空间,一共拷贝5个int数,共20个字节。

int arr1[] = {
    
     1,2,3,4,5,6,7,8,9 };
	int arr2[10] = {
    
     0 };


	my_memcpy(arr1 + 4, arr1 + 2, 20); //在vs编译器以下,memcpy也实现了

	int i;
	for (i = 0;i < 9;i++)
	{
    
    
		printf("%d ", arr1[i]);
	}

此时调用上面写的memcpy函数,打印dest,输出:1 2 3 4 3 4 3 4 3,此时arr1数组为下图:

在这里插入图片描述

当使用memcpy函数对有内存重叠程序进行拷贝出问题了。

为什么呢,当src给dest进行拷贝,3赋值给了5,4赋值给了6,数组原来5,6的位置的数据已经被改成了3,4。
在这里插入图片描述
有什么办法解决这个问题呢。之前都是从前向后拷贝,这时候尝试一下从后向前拷。

在这里插入图片描述
从后向前拷贝解决了内存重叠尝试的问题。但是此时的dest指针是大于src指针的,如果dest指针小于src指针又是什么样。


此时dest在src右边。从前往后拷贝,不会出现问题。

在这里插入图片描述

从后往前拷贝,此时又出现了内存重叠问题。
在这里插入图片描述

我们可以得出结论。如果dest小于src,就从做前向后拷贝。dest大于src,就从后向前拷贝。如果不存在内存重叠问题,那么使用哪种方式都可以。
在这里插入图片描述
代码从后向前拷贝如何实现。

当前平台vs2019是小端存储模式。
在这里插入图片描述

假设,如图进行拷贝。src + count - 1,dest+ count - 1我们就得到了从后向前拷贝的第一个字节。
在这里插入图片描述

代码实现(示例):

void* my_memmove(void* dest, const void* src, size_t sum)
{
    
    
	assert(dest && src);

	void* res = dest;

	if (dest < src)///如果dest小于src,从前向后拷贝。
	{
    
    
		while (sum--)
		{
    
    
			*(char*)dest = *(char*)src;

			++(char*)dest;
			++(char*)src;
		}
	}
	else //大于或不重叠从后先前拷。
	{
    
    
		while (sum--) //第一次sum =19 ,当sum = 0 停止拷贝
		{
    
    
			*((char*)dest + sum) = *((char*)src + sum);
		}
	}

	return res;
}

猜你喜欢

转载自blog.csdn.net/juggte/article/details/115023820
今日推荐