C语言-memmove(重复地址拷贝模拟实现)(包含动图)

 语法讲解 

baa6475324f6435188e6e3d3d6d62d48.png

 C语言中的`memmove`函数是一个非常有用的内存操作函数,它可以将一段内存内容从一个位置移动到另一个位置,即使在源内存和目标内存有重叠的情况下,它也能正确地完成内存复制工作。

函数原型定义如下:
```c
void *memmove(void *dest, const void *source, size_t n);
```
参数说明:
- `dest`:指向目标内存的指针,这里将复制数据到这个地址空间。
- `source`:指向源内存的指针,这里是要复制的数据所在的位置。
- `n`:要复制的字节数。

返回值:
- 函数成功执行后,返回指向`dest`的指针。
- 如果参数指向的内存区域非法,则返回`NULL`。
`memmove`的工作原理是,如果源地址和目标地址是不重叠的,那么它就像`memcpy`函数一样工作,直接复制数据。如果源地址和目标地址重叠,`memmove`会采取一种高效的方法,从后向前复制数据,以避免在复制过程中覆盖数据。
使用示例:

```c
#include <stdio.h>
#include <string.h>
int main() {
    char source[] = "Hello, World!";
    char dest[20];
    // 确保目标数组有足够的空间来存放源字符串
    strcpy(dest, source);
    // 使用memmove将source字符串移动到dest数组的前面
    memmove(dest, source, strlen(source) + 1);
    printf("Copied string: %s\n", dest);
    return 0;
}
```

在上面的示例中,我们首先用`strcpy`将字符串复制到`dest`数组,然后使用`memmove`将`source`数组的内容移动到`dest`数组的前面。注意,这里我们使用了`strlen`函数来获取字符串长度,并加上1来确保还包括字符串结束符`\0`。
需要注意的是,在使用`memmove`时,要确保移动操作不会导致内存访问越界,特别是在处理动态分配的内存或者指针运算时要格外小心。 

memcpy和memmove的区别

memcpy的逻辑

ba2515ef55724b578695e661c4d5f46a.png

memmove的逻辑

这个函数和memcpy其实是差不多的 ,本质上都是拷贝函数地址从而达到目的,但是需要知道的是memcpy是不能完成重复函数的拷贝的 什么意思呢 简单的说就是,重叠就交给memove不成重叠交给memcpy。9d562cc04f95434f84fb8446e4906493.png

第一种情况 把同一个数组里面的 1 2 3  拷贝到2 3 4 里面去这个时候就会产生问题 也就是 
我们需要的是 1 1 2 3 5 6 7 8 
但是实际我们得到的可能是 1 1 1 1 5 6 7 8 
也就是1 拷贝给2,2 变成1,2->1拷贝给3,此时3也变成1,但是实际我们需要的是1 1 2 3 5 6 7 8所以是不能满足目的的,如果有些编译器可以满足拷贝要求 ,那更大的可能就是,memcpy和memmove函数一样。

也就是

1

6f3af895f9a54934afa6234f94f40b3c.png

2

 7f4d74f6b9874fd68e3b591679ae25ae.png

图解(原理讲解)

也就是此时存在两种情况,一种是被拷贝空间在拷贝空间前面,一种是被拷贝空间在拷贝空间后面, 这个时候我们提出两种解决方式,也就是一种是从前向后拷贝,一种是从后向前拷贝

1,需要把前面的空间拷贝到后面去,并且不能覆盖和重复

当被拷贝函数在前面的时候,我们分析两种图解

第一种是从前向后拷贝(这里的从前向后拷贝是第一个拷贝到最后一个)(不满足)
 

第二种是从后向前拷贝(这里的从后向前拷贝是被拷贝函数的从最后一个元素拷贝到第一个元素)(满足)

2当被拷贝函数在后面的时候,我们分析两种图解

这里也是两种情况

第一种从前往后拷贝(满足)

第二种从后往前拷贝 不满足

画图总结

地址是从低地址到高地址

也就是

低----------------------------------------->高

拷贝重复地址的模拟实现

这个不像memcpy 拷贝不重复地址 memcpy就算完成任务 也是超额完成

这里的完成任务不算超额完成

5*sizeof(int) 这里是20个字节大小

这里是 sizeof(int)四个字节 这里传递的是字节

9a28c7948634470cb95913c43df7c2e2.png

c6562210e2534ec3ab3d6b8a8f6348e0.png

模拟函数

16ca2c689242465ab72885b594f1565e.png

图解

把1 2 3 4 5 拷贝到 3 4 5 6 7

34b208bfe090400f95ec8fc9006d646a.png

版本1(不是最优解)

b38f3908d45145d6826182ae6fec5add.png

版本2(最优解)

c5b407c5a30247929a457e576d87cbab.png

忘记知识是一个客观事实 但是思考方式是不会忘记的

4e0a15ddee0442b18aa89ec8709c9fe2.png

地址从低地址到高地址

6a3c6710ae4d4a68bf4296465153310f.png

30c424646172417aaa360536eef35133.png

第二个是从前到后

第一个是从后到前

eed372ddc743491192c01ff3fbc43f3c.png

所以

代码总结

#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>
#include<string.h>
#include<stdio.h>
#include<stdlib.h>
void* my_memmove(void* dest,const void* src, size_t num)
{
	assert(dest && src);
	if (src > dest)
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			src = (char*)src + 1;
			dest = (char*)dest + 1;
		}
	}
	else
	{
		while (num--)
		{
			//这里可以这样理解 也就是 本质还是每次许循环都是相等的 
			//但是因为是从后向前拷贝的 所以此时是
			// 当 scr指向8 的时候 他的最后数值的时候
			// dest刚好指向的也是最后一个数值
			// 因为是首元素地址+num 这个num是20个字节 也就是武哥元素 也就是同时他们指向的都是各自需要交换的最后一个字节
			//最后再每次进行-- 直到循环结束 此时结束循环
			*((char*)dest + num) = *((char*)src + num);
		}
	}
}
void* MY_memmove(void* dest, const void* scr, size_t num)
{
	assert(dest && scr);
	//首先我们需要知道 地址是从低地址到高地址进行的 
	// 也就是我们需要知道 什么时候scr 小于dest 什么时候大于dest 根据这个计算 就可以得到想要的结果
	// 我们需要知道 scr 是被拷贝的空间 dest  是拷贝的空间
	if (scr > dest)
	{
		while (num--)	//这里还是每次减少一个字节 这里是被拷贝空间在拷贝空间后面 所以其实也发现并没有什么变化 那么也是可以把memcpy的函数直接拿过来
		{
			*(char*)dest = *(char*)scr;
			scr = (char*)scr + 1;
			dest = (char*)dest + 1;
		}
	}
	else//这里包含小于等于 也可以不包含等于  这里是scr<dest也是就需要从后往前进行打印
	{
		while (num--)
		{
			*((char*)dest + num) = *((char*)scr + num);//这里的意思是
		}
	}
}
int main()
{
	char arr1[] = "dfskbjh ";
	char arr2[100] = { 0 };
	memmove(arr2, arr1, 7);
	printf("%s \n", arr2);

	char arr3[] = "dfskbjh ";
	char arr4[100] = { 0 };
	memmove(arr4, arr3, 5 * sizeof(char));
	printf("%s \n", arr4);

	int arr5[] = { 1,2,3,4,5,6,7,8,9,1 };
	int arr6[100] = { 0 };
	memmove(arr6, arr5, 20);//这里是把不重复地址复制到arr6里面 20个字节也就四个数值
	for (int i = 0; i < 20; i++)
	{
		printf("%d ", arr6[i]);
	}
	printf("\n");


	int arr7[] = { 1,2,3,4,5,6,7,8,9,1 };
	memmove(arr7, arr7 + 5, 5 * sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr7[i]);//这里是直接打印数组
	}
	printf("\n");
	int arr8[] = { 1,2,3,4,5,6,7,8,9,1 };
	memmove(arr8 + 2, arr8, 5 * sizeof(int));
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr7[i]);//这里是直接打印数组
	}
	printf("\n\n\n");

	int arr9[] = { 1,2,3,4,5,6,7,8,9,1 };
	my_memmove(arr9 + 2, arr9, 12);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", arr9[i]);//这里是直接打印数组
	}
	printf("\n\n\n");
	 
	int str1[] = { 1,2,3,4,5,6,7,8,9,1 };
	MY_memmove(str1, str1+2, 20);
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", str1[i]);
	}
	printf("\n\n\n");

	return 0;
}

  • 19
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值