memcpy()vs memmove()

use*_*785 151 c memcpy memmove

我想了解的区别memcpy()memmove(),和我读的文本memcpy(),而没有照顾重叠源和目的地memmove()一样.

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

有没有更好的例子来理解它的缺点memcpymemmove解决方法?

// 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 );
}
Run Code Online (Sandbox Code Playgroud)

输出:

The string: aabbcc
New string: aaaabb
The string: aabbcc
New string: aaaabb
Run Code Online (Sandbox Code Playgroud)

dev*_*ity 117

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

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

Memmove做了更多工作以确保它正确处理重叠.

编辑:

(不幸的是,我找不到像样的例子,但这些都可以).对比此处显示的memcpymemmove实现.memcpy只是循环,而memmove执行测试以确定循环的方向以避免破坏数据.这些实现相当简单.大多数高性能实现都比较复杂(涉及一次复制字大小块而不是字节).

  • @Alcott:因为`memcpy`可以更快. (29认同)
  • memcpy没有处理重叠问题,但memmove确实如此.那为什么不从lib中消除memcpy呢? (3认同)
  • +1另外,在下面的实现中,`memmove`在测试指针后在一个分支中调用`memcpy`:http://www.student.cs.uwaterloo.ca/~cs350/common/os161-src-html/memmove_8c -source.html (2认同)

rxa*_*tos 90

内存中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. 
Run Code Online (Sandbox Code Playgroud)

memcpy的某些实现可能仍然适用于重叠输入,但您无法计算该行为.虽然memmove必须允许重叠.

  • 它真的帮助了我!+1为您的信息 (3认同)

Bil*_*eal 32

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

  • 特别是,根据平台的不同,`memcpy`的实现方式可能与`memmove`完全相同.也就是说,编写编译器的人并没有费心去编写一个独特的`memcpy`函数. (10认同)

Nei*_*val 20

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 );

}
Run Code Online (Sandbox Code Playgroud)

得到:

The string: abcdef
New string: abcdefabcdefabcd
The string: abcdef
New string: abcdefabcdef
Run Code Online (Sandbox Code Playgroud)

  • 恕我直言,这个示例程序有一些缺陷,因为 str1 缓冲区的访问超出范围(要复制 10 个字节,缓冲区大小为 7 个字节)。越界错误会导致未定义的行为。memcpy()/memmove() 调用显示结果的差异是特定于实现的。而且示例输出与上面的程序并不完全匹配...另外,strcpy_s() 不是标准 C AFAIK 的一部分(MS 特定的,另请参阅:/sf/ask/2570676251/ strcpy-s-not-exist-anywhere-on-my-system/36724095#36724095) - 如果我错了,请纠正我。 (3认同)

小智 7

由于"坏"编译器,你的演示没有暴露memcpy的缺点,它在Debug版本中帮你一个忙.但是,发布版本为您提供相同的输出,但是由于优化.

    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  
Run Code Online (Sandbox Code Playgroud)

%eax此处的寄存器用作临时存储,"优雅地"修复重叠问题.

复制6个字节时会出现这个缺点,至少是它的一部分.

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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

The string: aabbccdd
New string: aaaabbbb
The string: aabbccdd
New string: aaaabbcc
Run Code Online (Sandbox Code Playgroud)

看起来很奇怪,它也是由优化引起的.

    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  
Run Code Online (Sandbox Code Playgroud)

这就是我memmove在尝试复制2个重叠内存块时总是选择的原因.


Cir*_*四事件 6

C11标准草案

C11 N1570标准草案说:

7.24.2.1“memcpy 函数”:

2 memcpy 函数将 s2 指向的对象中的 n 个字符复制到 s1 指向的对象中。如果复制发生在重叠的对象之间,则行为未定义。

7.24.2.2“memmove 函数”:

2 memmove函数将s2指向的对象中的n个字符复制到s1指向的对象中。复制的过程就好像首先将 s2 指向的对象中的 n 个字符复制到一个包含 n 个字符的临时数组中,该数组不与 s1 和 s2 指向的对象重叠,然后将临时数组中的 n 个字符复制到s1指向的对象

因此,任何重叠都会memcpy导致未定义的行为,并且任何事情都可能发生:坏的,什么都没有,甚至是好的。好东西很少见:-)

memmove然而,清楚地表明一切都会发生,就好像使用了中间缓冲区一样,因此显然重叠是可以的。

然而, C++std::copy更宽容,并且允许重叠:std::copy 是否处理重叠范围?