C语言进阶——模拟实现memmove函数

模拟实现memmove函数

一、函数的声明

void * memmove ( void * destination, const void * source, size_t num );
  1. 函数的第一个参数为:指向目标空间的指针。
  2. 函数的第二个参数为:指向源头空间的指针。
  3. 函数的第三个参数为:无符号整型类型的num。
  4. 函数的返回值的类型为:void*,返回的是类型的指针。如图:

解释说明:destination 和 source都为void* 类型和memcpy同理,这是因为,作为memmove的实现者,他并不知道将来这里传入的参数是什么类型,所以用void* 来接收,无论什么类型都能接收。
source 前面用const修饰,因为memmove并不需要源头空间被修改
函数的返回值类型同样为void*,这是因为函数返回的是目标空间的起始地址,所以与destination指针的类型相同。

最后一个参数为无符号整型,size_t 整型永远 >0,这是因为num传入的是我们需要拷贝的字节数,我们不可能拷贝负数个字节数,所以这里用无符号整型。
上面这一部分在讲memcpy时同样提到。

二、函数的作用

把source指针指向的空间拷贝到destination指针指向的空间去,拷贝num个字节,这里我们需要注意:内存函数操作的单位均为字节。

memmove和字符串函数(例如:strcpy)不同,遇到\0并不会停止拷贝,它并不关注内存中存放的是什么,只是按照给定的字节数拷贝。

上篇提到memcpy不能用于两个发生重叠的空间的拷贝,那么memmove就是专门用于解决发生重叠的空间的拷贝问题的。

memmove一般用于两个发生重叠的空间的拷贝

三、函数的使用

//memmove的使用

int main()
{
	int arr1[20] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	memmove(arr1, arr1 + 2, 20);//将从arr1+2开始向后20字节的的数据,拷贝到arr1处去
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

将arr1数组下标为23456的空间拷贝到下标为01234的空间去
在这里插入图片描述
在这里插入图片描述
调用memmove之前arr1中存放的数据是这样的。

调用memmove之后arr1+2向后20个字节的内容成功的被拷贝到了arr1处
在这里插入图片描述

四、函数的模拟实现

在模拟实现之前我先来举例两种场景
如果我们想把arr处的内容拷贝到arr+2处:
如果我们按照从前向后拷贝的话,就会导致前面的数据被覆盖,当想把3拷贝到原来5的位置去的时候,我们可以发现3已经被前面拷贝的1给覆盖了
由此我们可以得出,当拷贝重叠空间的数据时候,拷贝的顺序和方式就很关键了
在这里插入图片描述
那同样的问题,我们从后向前拷贝会怎么样呢?
我们可以发现,从后向前同样发生了覆盖,只是这一次拷贝3把5覆盖的时候,5已经提前拷贝到7的位置去了,尽管被覆盖,但是并没有发生数据的丢失
在这里插入图片描述
我们是不是可以得到一个结论,当destination指向的空间的起始地址,也就是destination指针指向的位置,也就是destination指针在source指针的左侧的时候,我们采取从后向前拷贝的方式或许更加靠谱一些。
举一反三:如果当destination在source右侧的时候,我们采取从前向后拷贝的方式,这里便不再做验证。
由于数组是从低地址到高地址存放的,所以 destination 在 source 左侧的意思就是destination < source
在这里插入图片描述
讲完了上面两种场景我们来进行memmove的模拟实现就得心应手了。
下面是模拟实现的代码,我在需要解释的代码的下一行进行了注释

//memmove 的模拟实现 -- memmove 用于内存重叠的拷贝
//让我们来实现我们自己的“my_memmove”吧!
void* my_memmove(void* dest, const void* src, size_t num)
//函数的参数和memmove的参数保持一致
{
	void* ret = dest; 
	//由于等下要返回dest的起始地址,并且会移动dest指针,所以这里先保存一下dest
	assert(dest && src);
	//断言,保证传入的dest和src指针不为空指针
	if (dest < src)//dest在src前边,从前向后拷贝
	{
		while (num--)
		{
			*(char*)dest = *(char*)src;
			//dest和src均为void*指针,不可以直接解引用
			//所以强制类型转化为char*指针,这里上一篇memcpy中有讲到
			//不懂的看一下上一篇,这里不做赘述
			dest = (char*)dest + 1;
			src = (char*)src + 1;
		}
	}
	else //dest在src后边,从后向前拷贝
	{
		while (num--)//num--完变成num-1,dest+num,刚好指向要拷贝的最后一个字节
		{
			*((char*)dest + num) = *((char*)src + num);
			//把src指向的空间向后num个字节的最后一个字节的内容
			//拷贝到dest指向的空间向后num个字节的最后一个字节去
		}
	}
	return ret;//返回刚开始保存好的dest的起始地址
}

//测试my_memmove

int main()
{
	int arr1[20] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	my_memmove(arr1, arr1+2, 20);//将arr2+2处向后20字节的数据,拷贝到arr1中去
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr1[i]);
	}
	return 0;
}

这里对从后向前拷贝那段代码画一个图解:
在这里插入图片描述
好了,以上就是本篇的全部内容了,感谢大家的观看!希望你能有所收获。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值