memcpy()vs memmove()

我想了解memcpy()memmove()之间的区别,并且我已经阅读了memcpy()不考虑重叠源和目标的文本,而memmove()却是这样。

但是,当我在重叠的内存块上执行这两个函数时,它们都会给出相同的结果。 例如,在memmove()帮助页面上采取以下MSDN示例: –

有没有更好的例子来理解memcpy的缺点,以及memmove如何解决它?

 // crt_memcpy.c // Illustrate overlapping copy: memmove always handles it correctly; memcpy may handle // it correctly. #include <memory.h> #include <string.h> #include <stdio.h> char str1[7] = "aabbcc"; int main( void ) { printf( "The string: %s\n", str1 ); memcpy( str1 + 2, str1, 4 ); printf( "New string: %s\n", str1 ); strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string printf( "The string: %s\n", str1 ); memmove( str1 + 2, str1, 4 ); printf( "New string: %s\n", str1 ); } 

输出:

 The string: aabbcc New string: aaaabb The string: aabbcc New string: aaaabb 

我并不完全感到惊讶,你的例子没有performance出奇怪的行为。 尝试将str1复制到str1+2 ,然后看看会发生什么。 (可能实际上没有什么区别,取决于编译器/库)。

一般来说,memcpy是以一种简单(但快速的方式)实现的。 简单地说,它只是循环遍历数据(按顺序),从一个位置复制到另一个位置。 这可能会导致源被读取时被覆盖。

Memmove做更多的工作,以确保它正确处理重叠。

编辑:

(不幸的是,我找不到像样的例子,但是这些都可以)。 对比这里显示的memcpy和memmove实现。 memcpy只是循环,而memmove执行一个testing来确定要循环的方向,以避免损坏数据。 这些实现相当简单。 大多数高性能实现更复杂(涉及一次复制字大小的块而不是字节)。

memcpy中的内存不能重叠,否则可能会导致未定义的行为,而memmove中的内存可能会重叠。

 char a[16]; char b[16]; memcpy(a,b,16); // valid memmove(a,b,16); // Also valid, but slower than memcpy. memcpy(&a[0], &a[1],10); // Not valid since it overlaps. memmove(&a[0], &a[1],10); // valid. 

memcpy的某些实现可能仍然适用于重叠的input,但是您不能计算该行为。 虽然眼镜必须允许重叠。

仅仅因为memcpy不必处理重叠区域,并不意味着它不能正确处理它们。 重叠区域的调用会产生未定义的行为。 未定义的行为可以完全按照您在一个平台上的预期工作; 这并不意味着它是正确的或有效的。

memcpy和memove都做类似的事情。

但要看出一个区别:

 #include <memory.h> #include <string.h> #include <stdio.h> char str1[17] = "abcdef"; int main() { printf( "The string: %s\n", str1 ); memcpy( (str1+6), str1, 10 ); printf( "New string: %s\n", str1 ); strcpy_s( str1, sizeof(str1), "aabbcc" ); // reset string printf( "The string: %s\n", str1 ); memmove( (str1+6), str1, 10 ); printf( "New string: %s\n", str1 ); } 

得到:

 The string: abcdef New string: abcdefabcdefabcd The string: abcdef New string: abcdefabcdef 

你的演示没有公开memcpy的缺点,因为“坏”的编译器,你在debugging版本的帮助。 然而,一个发布版本给你相同的输出,但是因为优化。

  memcpy(str1 + 2, str1, 4); 00241013 mov eax,dword ptr [str1 (243018h)] // load 4 bytes from source string printf("New string: %s\n", str1); 00241018 push offset str1 (243018h) 0024101D push offset string "New string: %s\n" (242104h) 00241022 mov dword ptr [str1+2 (24301Ah)],eax // put 4 bytes to destination 00241027 call esi 

寄存器%eax在这里作为一个临时存储,它“优雅地”修复了重叠问题。

在复制6个字节时出现缺陷,至less是其中的一部分。

 char str1[9] = "aabbccdd"; int main( void ) { printf("The string: %s\n", str1); memcpy(str1 + 2, str1, 6); printf("New string: %s\n", str1); strcpy_s(str1, sizeof(str1), "aabbccdd"); // reset string printf("The string: %s\n", str1); memmove(str1 + 2, str1, 6); printf("New string: %s\n", str1); } 

输出:

 The string: aabbccdd New string: aaaabbbb The string: aabbccdd New string: aaaabbcc 

看起来很奇怪,也是由优化造成的。

  memcpy(str1 + 2, str1, 6); 00341013 mov eax,dword ptr [str1 (343018h)] 00341018 mov dword ptr [str1+2 (34301Ah)],eax // put 4 bytes to destination, earlier than the above example 0034101D mov cx,word ptr [str1+4 (34301Ch)] // HA, new register! Holding a word, which is exactly the left 2 bytes (after 4 bytes loaded to %eax) printf("New string: %s\n", str1); 00341024 push offset str1 (343018h) 00341029 push offset string "New string: %s\n" (342104h) 0034102E mov word ptr [str1+6 (34301Eh)],cx // Again, pulling the stored word back from the new register 00341035 call esi 

这就是为什么我总是在试图复制2个重叠的内存块时selectmemmove

memcpymemmove的区别在于

  1. memmove ,指定大小的源内存被复制到缓冲区,然后移动到目的地。 所以如果记忆是重叠的,没有副作用。

  2. memcpy()情况下,没有为源内存采取额外的缓冲区。 复制是直接在内存上完成的,所以当有内存重叠时,我们会得到意想不到的结果。

这些可以通过下面的代码来观察:

 //include string.h, stdio.h, stdlib.h int main(){ char a[]="hare rama hare rama"; char b[]="hare rama hare rama"; memmove(a+5,a,20); puts(a); memcpy(b+5,b,20); puts(b); } 

输出是:

 hare hare rama hare rama hare hare hare hare hare hare rama hare rama 

正如在其他答案中已经指出的, memmovememcpy更复杂,因此它占用了内存重叠。 memmove的结果被定义为如果src被复制到一个缓冲区,然后缓冲区复制到dst 。 这并不意味着实际的实现使用任何缓冲区,但可能做一些指针算术。

编译器可以优化memcpy,例如:

 int x; memcpy(&x, some_pointer, sizeof(int)); 

这个memcpy可以优化为: x = *(int*)some_pointer;

在memcpy的链接http://clc-wiki.net/wiki/memcpy中给出的代码似乎让我有点困惑,因为当我使用下面的例子实现它时,它不会给出相同的输出。;

 #include <memory.h> #include <string.h> #include <stdio.h> char str1[11] = "abcdefghij"; void *memcpyCustom(void *dest, const void *src, size_t n) { char *dp = (char *)dest; const char *sp = (char *)src; while (n--) *dp++ = *sp++; return dest; } void *memmoveCustom(void *dest, const void *src, size_t n) { unsigned char *pd = (unsigned char *)dest; const unsigned char *ps = (unsigned char *)src; if ( ps < pd ) for (pd += n, ps += n; n--;) *--pd = *--ps; else while(n--) *pd++ = *ps++; return dest; } int main( void ) { printf( "The string: %s\n", str1 ); memcpy( str1 + 1, str1, 9 ); printf( "Actual memcpy output: %s\n", str1 ); strcpy_s( str1, sizeof(str1), "abcdefghij" ); // reset string memcpyCustom( str1 + 1, str1, 9 ); printf( "Implemented memcpy output: %s\n", str1 ); strcpy_s( str1, sizeof(str1), "abcdefghij" ); // reset string memmoveCustom( str1 + 1, str1, 9 ); printf( "Implemented memmove output: %s\n", str1 ); getchar(); } 

输出:

 The string: abcdefghij Actual memcpy output: aabcdefghi Implemented memcpy output: aaaaaaaaaa Implemented memmove output: aabcdefghi 

但是你现在可以明白为什么眼镜会照顾重叠的问题。

我已经尝试使用eclipse运行相同的程序,它显示了memcpymemmove之间的明显区别。 memcpy()不关心导致数据损坏的内存位置重叠,而memmove()会首先将数据复制到临时variables,然后复制到实际的内存位置。

当试图从位置str1str1+2复制数据时, memcpy输出是“ aaaaaa ”。 问题将是如何? memcpy()会从左到右每次复制一个字节。 如你的程序“ aabbcc ”所示,那么所有的复制都将发生如下,

  1. aabbcc -> aaabcc

  2. aaabcc -> aaaacc

  3. aaaacc -> aaaaac

  4. aaaaac -> aaaaaa

memmove()将首先将数据复制到临时variables,然后复制到实际的内存位置。

  1. aabbcc(actual) -> aabbcc(temp)

  2. aabbcc(temp) -> aaabcc(act)

  3. aabbcc(temp) -> aaaacc(act)

  4. aabbcc(temp) -> aaaabc(act)

  5. aabbcc(temp) -> aaaabb(act)

输出是

memcpyaaaaaa

memmoveaaaabb