1.准备阶段
不同于strcpy的只针对于字符串进行拷贝,memcpy是针对于内存进行拷贝,可以对整形,长整型,结构体等类型进行拷贝,更加灵活通用。
但是由于我们不知道使用这个函数的用户会拷贝什么类型的数据,所以我们需要用void*指针来接收地址,然后根据传进来的num(也就是需要拷贝的总字节数),来进行一个字节一个字节的拷贝。
观察一下这张图片↑
1.首先这个dest指针,就是我们传入的目标地址(拷贝到的地址)
2.这个src指针,就是即将被拷贝的地址
3.num表示要拷贝的总字节数
2.实现原理
①.正常情况
正常情况下,也就是内存不重叠的地方,实现起来并不困难,就是根据src的位置一个字节一个字节地往前覆盖,如下图
②.特殊情况
情况1
为了方便描述,我默认拷贝 3 个字节,并且默认将src为起始位置拷贝到dest中
这种情况下,src>dest,并且有内存重叠的部分,按照我们的常规思路,就是类似于strcpy一样,根据src的位置依次往前拷贝,如下:
情况2
为了方便描述,我默认拷贝 3 个字节,并且默认将src为起始位置拷贝到dest中
但是呢,在某种特殊情况(即内存重叠)中,按照正常的拷贝逻辑就可能没法实现预想中的功能。
比如这种情况,src<dest,我们计划将src后面三个字节的内容拷贝到dest中,dest后面三个字节拷贝完成后本应该是:abc
但实际上:
原本计划dest开始的三个字节应该是abc,但是实际上却是aba,一开始的c就被直接覆盖了。
对于这种情况,我们应该反着拷贝,也就是这样:↓
规律总结:
根据正常情况和两个特殊情况,我们可以得出以下结论
1.如果src<dest(也就是src在dest左边的时候),那么我们就要从后往前覆盖
2.其他情况下,从前往后覆盖都可以成功拷贝
3.完整代码
void* myMemmove(void* dest,void* src, size_t num) //这里的num表示的是拷贝的总字节数
{
const char* ret = dest; //记录dest的起始位置便于返回
assert(dest && src); //断言
if (dest < src) //情况1
{
while (num--)
{
*((char*)dest) = *((char*)src);
dest = (char*)dest + 1;
src = (char*)src + 1;
}
}
else //情况2
{
while (num--) //判断完真假后--
{
*((char*)dest + num) = *((char*)src + num);
}
}
return ret; //返回起始位置指针
}
部分代码细节
while (num--)
{
*((char*)dest) = *((char*)src);
dest = (char*)dest + 1;
src = (char*)src + 1;
}
正如文章开头所讲,我们不知道使用这个函数的用户将会传入什么类型的数据,所以用void接收后的覆盖过程,我们需要一个字节一个字节地覆盖,所以需要强转成char。
其次,为了下一个字节的覆盖,dest和src都要强转成char*后再+1.