memcpy与memmove的区别

这两个函数都是将s2指向位置的n字节数据拷贝到s1指向的位置。memcpy 假定两块内存区域没有数据重叠,而 memmove 没有这个前提条件。如果复制的两个区域存在重叠时使用memcpy,其结果是不可预知的( 未定义的 ),有可能成功也有可能失败。原因如下:

  • 当src地址大于dest地址时,即使有重叠,也可以正常复制。

  • 当src地址小于dest地址时,若重叠,则复制发生异常。

    这种情况下,src的地址小于dest的地址,拷贝前3个字节没问题,但是拷贝第4,5个字节时,原有的内容已经被src拷贝过来的字符覆盖了,所以已经丢失原来src的内容。

    然而,vs2019下进行试验,发现即使是在第二种情况下,memcpy与memmove的结果相同,都正确,原因可能是 memcpy 目前也采用了 memmove 的方式。

memmove的实现:

1
2
3
4
5
6
7
8
9
10
11
12
void *memmove(void *dest, const void *src, size_t n)
{
unsigned char *pd = dest;
const unsigned char *ps = src;
if((ps + n > pd)&& ps < pd)
for (pd += n, ps += n; n--;)
*--pd = *--ps;
else
while(n--)
*pd++ = *ps++;
return dest;
}

memmove会对拷贝的数据作检查,确保内存没有覆盖,如果发现会覆盖数据,简单的实现是调转开始拷贝的位置,从尾部开始拷贝但是实际在 C99 实现中,是将内容拷贝到临时空间,再拷贝到目标地址中

1
2
3
4
5
6
7
void *memmove(void *dest, const void *src, size_t n)
{
unsigned char tmp[n];
memcpy(tmp,src,n);
memcpy(dest,tmp,n);
return dest;
}

memcpy的实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void *memcpy(void *dest, const void *src, size_t n)
{
char *dp = dest;
const char *sp = src;
while (n--)
*dp++ = *sp++;
return dest;
}
//目前memcpy已做改进,以下为源码:
void *memcpy(void *dst, const void *src, size_t len)
{
if(NULL == dst || NULL == src){
return NULL;
}

void *ret = dst;

if(dst <= src || (char *)dst >= (char *)src + len){
//没有内存重叠,从低地址开始复制
while(len--){
*(char *)dst = *(char *)src;
dst = (char *)dst + 1;
src = (char *)src + 1;
}
}else{
//有内存重叠,从高地址开始复制
src = (char *)src + len - 1;
dst = (char *)dst + len - 1;
while(len--){
*(char *)dst = *(char *)src;
dst = (char *)dst - 1;
src = (char *)src - 1;
}
}
return ret;
}