【C语言】memmove函数的实现

函数介绍

memmove是一个用于拷贝一块区域的内存到另外一块区域的函数,定义如下

void * memcpy ( void * destination, const void * source, size_t num );

返回值:返回destination

参数介绍:destination,指向数据要被复制到的目的地

source,从何处拷贝数据,指向数据的来源地

num,你要拷贝的字节数

下面是一个memmove的使用例子,它将arr1里的前12个字节复制到了arr2里的前12个字节,最后进行打印

int main() {
    
    
	int arr1[] = {
    
     1,5,6,4,8,9 };
	int arr2[20] = {
    
     0 };
	memmove(arr2, arr1, 12);
	for (int i = 0; i < 3; i++) {
    
    
		printf("%d", arr2[i]);
	}
	return 0;
}

memmove的模拟实现

首先就是实现一个一个字节的复制,那么我们就将指针转换为char*然后再执行一个次数为num次的循环,然后依次进行复制粘贴即可。同时为了能够返回我们的dest位置,我们刚开始可以将其存储在一个指针变量里,并且在最后将其返回

void* my_memmove(void* dest, const void* src, size_t num) {
    
    
	void* ret = dest;
	for (int i = 0; i < num; i++) {
    
    
		*((char*)dest) = *((char*)src);
		(char*)src = (char*)src + 1;
		(char*)dest = (char*)dest + 1;
	}
	return ret;
}

但是我们在使用上面代码的时候就会发现一个问题:如果复制粘贴的内存存在重叠的部分,就会发生问题

那么上面的代码会产生这样的问题呢?让我们以下面代码为例分析一下

void* my_memcpy(void* dest, const void* src, size_t num) {
    
    
	assert(dest && src);
	void* ret = dest;
	for (int i = 0; i < num; i++) {
    
    
		*((char*)dest) = *((char*)src);
		(char*)src = (char*)src + 1;
		(char*)dest = (char*)dest + 1;
	}
	return ret;
}
int main() {
    
    
	int arr[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	my_memcpy(arr+2, arr, 20);
	for (int i = 0; i < 10; i++) {
    
    
		printf("%d ", arr[i]);
	}
	return 0;
}

我们上面的这串代码,理想输出应该是1 2 1 2 3 4 5 8 9 10,但是实际上输出的是1 2 1 2 1 2 1 8 9 10

那么是为什么呢?实际上因为我们在还没有转移第三个元素的时候,第三个元素的已经被修改为了1,那么后面发生的也是同样的问题

但是假如我们将上面代码的arr+2arr交换,将后面的放到前面来,即使内存重叠也不会发生错误,这又是为什么呢?

正是因为复制顺序的问题

在这里插入图片描述

我们可以发现,当重叠部分是dest的前面src的后面时。那么当我们从前往后复制的时候,就会把即将复制的3 4 替换成1 2然后后面就会一直循环复制1 2

但是倘若交换destsrc,那么重叠的部分就是dest的后面src的前面,那么那个时候从前往后复制,就会先替换dest中未重叠的部分,就不会导致我们src的内容在还没复制的时候就被改变

那么有没有办法能让重叠部分是dest的前面src的后面时也能完成复制任务呢?我们不妨想一想,我们能不能让arr->arr+2的复制方向反过来从后往前复制,那这样在执行复制过程的时候不也就相当于先替换dest中未重叠的部分了吗?

所以实际上我们只要让函数在遇到dest的前面src的后面的这种情况时,从后往前复制那就可以完美完成任务。那么我们怎么区分这两个情况呢?

其实我们就只要通过比较destsrc指向的位置的位置就可以区分两种情况了,当src位于dest左侧时(也就是src<dest时)就用从后往前的复制方式。反之,就用从前往后的复制方式

当不重叠的时候,无论用什么方法都无所谓了,那么就让它继续沿用重叠时候的复制方法即可

void* my_memmove(void* dest, const void* src, size_t num) {
    
    
	assert(dest && src);
	void* ret = dest;
	if (dest < src) {
    
    
		for (int i = 0; i < num; i++) {
    
    
			*((char*)dest) = *((char*)src);
			(char*)src = (char*)src + 1;
			(char*)dest = (char*)dest + 1;
		}
	}//这种情况用原来的函数就可以,直接复制粘贴
	else {
    
    
		while (num--) {
    
    
			*((char*)dest + num) = *((char*)src + num);
		}
		//让他+num 然后我们只需num--就能实现从后往前的复制
	}
	return ret;
}

那么这样就不会产生由于内存重叠而导致复制结果不正确的问题

猜你喜欢

转载自blog.csdn.net/qq_42150700/article/details/130042738
今日推荐