两个函数都包含于<string.h>头文件中

首先看memcpy函数的定义

1
void *memcpy( void *dest, const void *src, size_t count );

dest表示目的地地址,用指针变量加以维护;同理,src表示源地址;count是一个无符号整型,表示将从源地址往后拷贝多少个字节大小的数据

再看memmove函数的定义

1
void *memmove( void *dest, const void *src, size_t count );

两者区别

我们发现两者传递的参数相同,而两者之不同,我们现在给出:
如果将要拷贝到的目标缓存区与原缓存区有重叠(overlap),则我们需要使用memmove函数;其他情况下,则两者皆可。
也就是说,memmove函数支持源缓冲区与目标缓冲区是同一块并且能够处理两者的重叠的情况。

请看库函数对于memmove函数的算法实现:

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
//Algorithm:

void * memmove(void * dst, void * src, size_t count)
{
void * ret = dst;

if (dst <= src || dst >= (src + count)) {
/*
* Non-Overlapping Buffers
* copy from lower addresses to higher addresses
*/
while (count--)
*dst++ = *src++;
}
else {
/*
* Overlapping Buffers
* copy from higher addresses to lower addresses
*/
dst += count - 1;
src += count - 1;

while (count--)
*dst-- = *src--;
}

return(ret);
}

以下图中,我们需要将src的四个字符拷贝至dest
那么,我们根据src与dest内存地址的相对位置来逐步进行变换
我们图中的蓝色箭头表示了从src中拷贝字节的顺序

  1. dest在内存低地址,src在内存高地址,两者没有重叠,我们从前向后对src进行拷贝

在这里插入图片描述



  1. 那么,我们将src稍微向左移动,与dest重叠,此时,就来到了我们的第二种情况,即dest在内存低地址,src在内存高地址,且两者有重叠
    在这里插入图片描述

这种情况与第1种情况是一致的。



  1. 那么我们再来看看何时需要从src的高地址向低地址拷贝,也就是src从后向前拷贝给dest的情况

好,我们的src与dest依旧存在重叠,但是与第2种情况不同的是,src位于低地址,dest位于高地址
在这里插入图片描述
此时,src从后往前进行拷贝显然是个好方法,避免了未拷贝数据的覆盖。




4. 我们再把src向左移动,此时,src与dest又没有了内存的重叠。此时从前向后还是从后向前拷贝其实是取决于你的心情了
在这里插入图片描述



  1. 最后,当我们的src再次向左移动,并且与dest相隔count个字节的距离时,库函数程序员把它归在了对src从前往后进行拷贝的情况
    在这里插入图片描述

总结

好了,是时候进行一下总结了。

当我们使用memmove函数时,memmove函数其实已经帮我们考虑到了缓冲区是否重叠的问题了,它允许我们把一个字符串内的内容向它自己进行拷贝。

memcpy则没有这么贴心了,memcpy不会对于重叠的情况加以区别

但是值得一提的是,如果我们使用VS的编译器,memcpy与memmove的实现方式其实是一致的,两者等价

我们可以这么说,memmovememcpy的升级版,memmove是更加安全的。