use*_*785 151 c memcpy 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 );
}
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做了更多工作以确保它正确处理重叠.
编辑:
(不幸的是,我找不到像样的例子,但这些都可以).对比此处显示的memcpy和memmove实现.memcpy只是循环,而memmove执行测试以确定循环的方向以避免破坏数据.这些实现相当简单.大多数高性能实现都比较复杂(涉及一次复制字大小块而不是字节).
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必须允许重叠.
Bil*_*eal 32
仅仅因为memcpy不必处理重叠区域,并不意味着它没有正确处理它们.具有重叠区域的调用会产生未定义的行为.未定义的行为可以完全按照您在一个平台上的预期工作; 这并不意味着它是正确或有效的.
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)
小智 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个重叠内存块时总是选择的原因.
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 是否处理重叠范围?