bdo*_*lan 154
有memcpy,目的地根本不能与源重叠.有了memmove它可以.这意味着memmove可能会略微慢一些memcpy,因为它不能做出相同的假设.
例如,memcpy可能始终将地址从低到高复制.如果目标在源之后重叠,则表示在复制之前将覆盖某些地址.memmove在这种情况下,会检测到这一点并从另一个方向复制 - 从高到低.但是,检查这个并切换到另一个(可能效率较低)算法需要时间.
nos*_*nos 31
memmove可以处理重叠的内存,memcpy不能.
考虑
char[] str = "foo-bar";
memcpy(&str[3],&str[4],4); //might blow up
Run Code Online (Sandbox Code Playgroud)
显然源和目标现在重叠,我们用"bar"覆盖"-bar".memcpy如果源和目标重叠,则使用未定义的行为,因此在这种情况下我们需要memmove.
memmove(&str[3],&str[4],4); //fine
Run Code Online (Sandbox Code Playgroud)
小智 13
之间的主要区别memmove()和memcpy()的是,在memmove()一个缓冲 -临时存储器-被使用,所以没有重叠的危险.另一方面,memcpy()直接将数据从源指向的位置复制到目标指向的位置.(http://www.cplusplus.com/reference/cstring/memcpy/)
请考虑以下示例:
#include <stdio.h>
#include <string.h>
int main (void)
{
char string [] = "stackoverflow";
char *first, *second;
first = string;
second = string;
puts(string);
memcpy(first+5, first, 5);
puts(first);
memmove(second+5, second, 5);
puts(second);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
正如您所料,这将打印出来:
stackoverflow
stackstacklow
stackstacklow
Run Code Online (Sandbox Code Playgroud)但在这个例子中,结果将不一样:
#include <stdio.h>
#include <string.h>
int main (void)
{
char string [] = "stackoverflow";
char *third, *fourth;
third = string;
fourth = string;
puts(string);
memcpy(third+5, third, 7);
puts(third);
memmove(fourth+5, fourth, 7);
puts(fourth);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
stackoverflow
stackstackovw
stackstackstw
Run Code Online (Sandbox Code Playgroud)这是因为"memcpy()"执行以下操作:
1. stackoverflow
2. stacksverflow
3. stacksterflow
4. stackstarflow
5. stackstacflow
6. stackstacklow
7. stackstacksow
8. stackstackstw
Run Code Online (Sandbox Code Playgroud)
假设您必须同时实现这两个,则实现可能如下所示:
void memmove ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src < (uintptr_t)dst) {
// Copy from back to front
} else if ((uintptr_t)dst < (uintptr_t)src) {
// Copy from front to back
}
}
void mempy ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src != (uintptr_t)dst) {
// Copy in any way you want
}
}
Run Code Online (Sandbox Code Playgroud)
这应该很好地解释了差异。memmove总是以这种方式进行复制,如果src和dst重叠仍然是安全的,而memcpy正如文档中所说的那样并不在乎memcpy,两个存储区一定不能重叠。
例如,如果memcpy复制是“从前到后”并且存储块按此对齐
[---- src ----]
[---- dst ---]
Run Code Online (Sandbox Code Playgroud)
复制的第一个字节src到dst已经破坏的最后一个字节的内容src,这些已被复制了。仅复制“从后到前”将导致正确的结果。
现在交换src并dst:
[---- dst ----]
[---- src ---]
Run Code Online (Sandbox Code Playgroud)
在这种情况下,“从前到后”复制是唯一安全的,因为复制“从后到前”将src在复制第一个字节时已经破坏其前部。
您可能已经注意到,memmove上面的实现甚至没有测试它们是否确实重叠,它只是检查它们的相对位置,但是仅此一项就可以确保副本安全。由于memcpy通常使用最快的方式在任何系统上复制内存,memmove通常将其实现为:
void memmove ( void * dst, const void * src, size_t count ) {
if ((uintptr_t)src < (uintptr_t)dst
&& (uintptr_t)src + count > (uintptr_t)dst
) {
// Copy from back to front
} else if ((uintptr_t)dst < (uintptr_t)src
&& (uintptr_t)dst + count > (uintptr_t)src
) {
// Copy from front to back
} else {
// They don't overlap for sure
memcpy(dst, src, count);
}
}
Run Code Online (Sandbox Code Playgroud)
有时,如果memcpy始终复制“从前到后”或“从后到前”,memmove则也可能memcpy在重叠的情况下使用,但memcpy甚至可能以不同的方式复制,具体取决于数据的对齐方式和/或要存储的数据量。复制,因此即使您测试了memcpy系统上副本的方式,也不能依靠该测试结果来始终正确。
在决定呼叫哪个电话时,这对您意味着什么?
除非您确定src并dst不会重叠,否则调用memmove总是会导致正确的结果,并且通常会尽可能快地满足您所需的复制案例。
如果您确定知道src并且dst不重叠,请致电,memcpy因为无论您要求哪个结果都没关系,在这种情况下两者都可以正常工作,但是memmove永远不会比memcpy您不幸的情况更快,甚至慢一点,所以您只能赢得电话memcpy。
简单地从ISO/IEC:9899标准中对其进行了很好的描述.
7.21.2.1 memcpy函数
[...]
2 memcpy函数将s2指向的对象中的n个字符复制到s1指向的对象中.如果在重叠的对象之间进行复制,则行为未定义.
和
7.21.2.2 memmove功能
[...]
2 memmove函数将s2指向的对象中的n个字符复制到s1指向的对象中.复制的过程就好像s2指向的对象中的n个字符首先被复制到n个字符的临时数组中,这些字符不与 s1和s2指向的对象重叠,然后临时数组中的n个字符被复制到s1指向的对象.
我通常根据问题使用哪一个取决于我需要什么功能.
在纯文本memcpy()中不允许s1和s2重叠,同时memmove().