前言
上一篇我们介绍了memcpy和strcpy的区别,以及memcpy模拟实现,但这两个库函数都有一个缺点,那就是不能自己复制自己的内容
例子
这有一个数组arr,其元素分别为1、2、3、4、5、6、7、8、9、10,我们想将1、2、3、4复制到2的后面,从而将数组arr变成1、2、1、2、3、4、7、8、9、10
用memcpy尝试
我们发现,跟我们预期的 1、2、1、2、3、4、7、8、9、10有出入。
错误原因
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
memcpy(arr + 2, arr, 16);
memcpy实现过程
void* my_memcpy(void* dest, void* src, size_t count)
{
void* ret = dest;
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
return ret;
}
当我们这样操作时,src一开始指向1,1被复制到了3的位置上,后面指向2,2被复制到4的位置上,当src指针指向原来的3时,指向的内容变成了1,又将1复制到了5的位置上,当src指向原来的4时,指向的内容变成了2,又将2复制到了6的位置上。所以就出错了。
用memmove尝试
我们发现目的达到了,说明memmove适用于内存发生重叠的情况。那么memmove是怎么实现的呢?
memmove的模拟实现
整段代码
void* my_memmove(void* dest, void* src, size_t count)
{
void* ret = dest;
if (dest < src)
{
while (count--)
{
*(char*)dest = *(char*)src;
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else
{
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
}
return ret;
}
memmove的定义
由图可知,memmove的返回值是目标地址,形势参数分别是(目标地址,源头地址,需要操作的字节数)
具体实现步骤
第一种情况(dest在src后),采用由后向前复制
由上面的错误分析,我们知道是因为后面要被复制过去的内容被更改了,还是用上面的例子做示范,我们从1开始复制的话,1会将3覆盖掉,进而导致想将3复制到到5的位置上时,实际上是将1复制到5的位置上。
那么我们如果从4开始复制呢?我们由后至前进行复制,将4复制到6,再将3复制到5,这样我们就不怕3、4被1、2覆盖掉了。
我们要想先将4复制到6,先得将src指向4,dest指向6,然后再进行交换。我们用加传过去的字节数来实现。
*((char*)dest + count) = *((char*)src + count);
我们想将指针前移,直接count减一就行,又因为我们要重复这一行为,所以我们使用while循环来实现。
while (count--)
{
*((char*)dest + count) = *((char*)src + count);
}
第二种情况(dest在src前),采用由前向后复制
如果dest在src前,我们还能用由后至前的方法复制吗?
例如,我们将3、4、5、6向前移动2次,也就是将1、2、3、4、5、6、7、8、9、10变成3、4、5、6、5、6、7、8、9、10
我们发现并没有成为我们想象当中的样子。
错误原因
依然是要被复制的内容在被复制之前就被更改了,这里先将6移动到4,5移动到3,想将4移动到2时,实际上复制过去的是6,以此类推...
所以我们要采用由前向后复制的方法(【C语言】字符串拷贝函数(strcpy)与内存拷贝函数的不同及内存拷贝函数(memcpy)的模拟实现一文里有)
总结
memcpy不能实现自己拷贝自己,也就是不适用于内存叠加的情况。我们用memmove便可以解决这个问题。
对于memmove的模拟实现,核心思想就是将会被覆盖的、要被复制的内容提前使用。这里分为两种情况,一种是被复制的内容地址在目标内容地址的前面,另一种就是被复制的内容地址在目标内容地址的后面,对于前一种情况,我们使用由后至前进行复制的方法,对于后一种情况,我们使用由前至后进行复制的方法(dest在src后就由后向前。dest在src前,就由前向后)。
到此这篇关于C语言模拟实现memmove的示例代码的文章就介绍到这了,更多相关C语言 memmove内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!