C语言模拟实现memmove的示例代码
ZHENGZJM 人气:0前言
上一篇我们介绍了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前,就由前向后)。
加载全部内容