模拟实现memmove函数
一、函数的声明
void * memmove ( void * destination, const void * source, size_t num );
- 函数的第一个参数为:指向目标空间的指针。
- 函数的第二个参数为:指向源头空间的指针。
- 函数的第三个参数为:无符号整型类型的num。
- 函数的返回值的类型为: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;
}
这里对从后向前拷贝那段代码画一个图解:
好了,以上就是本篇的全部内容了,感谢大家的观看!希望你能有所收获。