在看Category的源码时碰见了两个C语言函数memmove
和memcpy
,在string.h
中我们可以看见它的定义:
void *memcpy(void *__dst, const void *__src, size_t __n);
void *memmove(void *__dst, const void *__src, size_t __len);
从名称定义上看memcpy
为拷贝,memmove
为移动,但是不要被它的名字给蒙蔽了,其实这两个函数的作用都是拷贝一定长度的内存内容。他们两个唯一的区别是:当内存发生局部重叠时memmove
函数能够保证拷贝结果的正确性,而memcpy
则不能保证拷贝结果的正确性;当内存没有发生重叠的时候两个函数的结果是一样的。
下面我们来通过图示了解一下这两个函数的区别:
内存没有发生重叠
dst和src内存区域不重叠时.png因为dst
和src
没有发生内存区域重叠,因此可以直接将src
拷贝到dst
的后面,并不会发生任何错误。
内存发生重叠
内存发生重叠有两种情况:dst
所在的区域在src
区域前面、dst
所在的区域在src
区域的后面。
当dst
所在的区域在src
区域前面时,memcpy
和memmove
是可以完成拷贝的,结果也是正确的:
当
dst
所在区域在src
前面时,将src
拷贝到dst
里面,只需要从src
的头部一个一个拷贝到dst
里面即可。当
dst
所在区域在src
后面时,我们看一下图:dst和src内存区域重叠时并且dst所在的区域在src区域后面时.png
此时如果还用
memcpy
函数的话,它会将src从头一个一个地拷贝到dst
下面,这时如果从src
的头部开始拷贝的话,会把内存地址为0x004
里面的内容覆盖掉,这一部分是dst
的开头但也是src
的一部分,因此此种情况下memcpy
函数会将重叠的部分覆盖掉;而在这种情况时memmove
会选择从src
的尾部开始拷贝(按箭头1、2、3、4),这样的话,重叠的内容就不会被覆盖,因此可以正确拷贝。我们在实际使用时如果在不确定内存地址是否重叠的情况下最好选择memmove
函数,它能保证结果的正确性。
函数内部实现
memmove
函数大体实现思路如下:
void *memmove(void *dest, const void *src, size_t count)
{
char *ret = dst;
if (src < dst)
{
dst += n;
src += n;
while (n--)
*--dst = *--src;
}
else
while (n--)
*dst++ = *src++;
return ret;
}
memcpy
函数大体实现思路如下:
void *memcpy(void *s1, const void *s2, register size_t n)
{
while (n--)
*dst++ = *src++;
return dst;
}
以上代码为参考simple_memmove
方法后得出的,具体的C标准库源码我们可以在这进行下载(打开网页后选择最新的glibc-x.x.x.tar.gz
进行下载查看)。
通过上述代码我们可以看到memmove
函数的一个分支为memcpy
函数的实现,而这一部分的实现就是内存地址没有发生重叠或者内存地址发生重叠并且dst
在src
区域前面时,而当内存地址发生重叠并且dst
在src
区域后面时memmove
执行了if
分支代码。
结束
虽然memmove
名称是移动,但是它和memcpy
函数一样,都是执行的拷贝。而之所以会有memcpy
函数的存在,是因为它的执行效率高,它里面没有像memmove
函数里面的if
分支,在我们能确定要拷贝的两个在内存地址没有重叠的情况下选择memcpy
还是比较理想的。
文章若有不足之处还请不吝赐教,大家互相学习。如果您觉得我的文章有用,点一下喜欢就可以了哦~~~。
网友评论