在介绍memcpy时,我们简单的实现了对memcpy的模拟,但是我们好像并没有想过如果拷贝数据与被拷贝数据之间有重叠时,我们自己实现的模拟memcpy函数还能正确拷贝吗?
实际上我们自己实现的memcpy函数终究还是不够全面的(库函数中的memcpy函数可能会成功这就是好比60分及格与80分以上优秀的差距了),平时拷贝有重叠的数据时我们一般使用的是memmove函数。
一、简介memmove
与memcpy函数一样,memmove函数也是库函数<string.h>中的,也是从source位置开始向后复制num个字节的数据到destination所处的内存位置。实际上两者之间的差别也就是memmove函数处理的源内存块和目标内存块是可以重叠的。
这里我们将arr1往后20个字节的数据复制到arr1+2的位置。
二、模拟实现memmove
假设我们要将1,2,3,4,5拷贝到3,4,5,6,7所在的内存块,我们想要的是
但是实际上
这是因为我们从前向后把1拷贝到3的位置,2拷贝到4所在的位置,那么这时3的位置的内容已经是1,那么拷贝到5的位置的数据就是1而不是3。开始将原始区域的内容复制到目标区域的时候,会将后半部分的原始区域的数据进行覆盖,造成原始数据的丢失。
那么我们如果从后向前复制呢?将5拷贝到7所在的位置,4拷贝到6所在的位置,以此类推可以顺利的将数据拷贝。
那么什么时候要从后向前拷贝呢?什么时候从前向后拷贝是合适的呢?
所以,当dest<=src的时候要从前向后拷贝,其他的时候从后向前拷贝(为了方便,其实也可以分为三份,dest<=src及dest与src无重合时可以从前向后,有重合时从后向前)。
具体代码的实现:
这里我们是分情况讨论的:
当dest<=src时 :数据从前向后拷贝的,因为不知道拷贝的时什么内容所以我们将dest与src都强转为char*类型,一个字节一个字节的去拷贝。拷贝完成后dest+1,src+1(跳到下一个字节)。
当dest>src时:
数据从后向前拷贝,这时我们将dest+num跳到要拷贝位置的末尾,再减1回到最后一位数的起始字节。src同理。(注意!dest这时被强转为char*类型,而1还是int类型)将src内的数据拷贝到dest,然后因为我们是从后向前存储,因此不是跳到下一个字节而是找到上个一个字节,所以dest-1,src-1;
最后返回dest即可,但是因为dest这时已经不在起始位置,因此我们定义一个void*类型的变量ret(因为不知到会是什么类型的数据)保存dest,然后 return ret即可。
将情况分为三份:
dest<=src时, 从前向后
dest与src无重合时,从前向后
dest与src有重合时,从后向前,
其实也是按从后向前还是从前向后的情况讨论的
dest<=src及dest与src无重合时讨论一种情况,有重合时从后向前一种情况