请考虑以下计划
/* Demonstrating memset(), memcpy(), and memmove(). */
#include <stdio.h>
#include <string.h>
char message1[60] = "Four score and seven years ago ...";
char message2[60] = "abcdefghijklmnopqrstuvwxyz";
char temp[60];
main()
{
printf("\nmessage1[] before memset():\t%s", message1);
memset(message1 + 5, '@', 10);
printf("\nmessage1[] after memset():\t%s", message1);
strcpy(temp, message2);
printf("\n\nOriginal message: %s", temp);
memcpy(temp + 4, temp + 16, 10);
printf("\nAfter memcpy() without overlap:\t%s", temp);
strcpy(temp, message2);
memcpy(temp + 6, temp + 4, 10);
printf("\nAfter memcpy() with overlap:\t%s", temp);
strcpy(temp, message2);
printf("\n\nOriginal message: %s", temp);
memmove(temp + 4, temp + 16, 10);
printf("\nAfter memmove() without overlap:\t%s", temp);
strcpy(temp, message2);
memmove(temp + 6, temp + 4, 10);
printf("\nAfter memmove() with overlap:\t%s\n", temp);
}
现在考虑两个不同编译器中的输出
ramesh@ramesh-K56CA:~/cpract$ ./a.out
message1[] before memset(): Four score and seven years ago ...
message1[] after memset(): Four @@@@@@@@@@seven years ago ...
Original message: abcdefghijklmnopqrstuvwxyz
After memcpy() without overlap: abcdqrstuvwxyzopqrstuvwxyz
After memcpy() with overlap: abcdefefefghijklqrstuvwxyz
Original message: abcdefghijklmnopqrstuvwxyz
After memmove() without overlap: abcdqrstuvwxyzopqrstuvwxyz
After memmove() with overlap: abcdefefghijklmnqrstuvwxyz
ramesh@ramesh-K56CA:~/cpract$ uname -a
Linux ramesh-K56CA 3.13.0-24-generic #47-Ubuntu SMP Fri May 2 23:30:00 UTC 2014 x86_64 x86_64 x86_64 GNU/Linux
现在考虑另一个编译器的输出
blc-10-6{8}: gcc memmove.c
blc-10-6{9}: ./a.out
message1[] before memset(): Four score and seven years ago ...
message1[] after memset(): Four @@@@@@@@@@seven years ago ...
Original message: abcdefghijklmnopqrstuvwxyz
After memcpy() without overlap: abcdqrstuvwxyzopqrstuvwxyz
After memcpy() with overlap: abcdefefefijijmnqrstuvwxyz
Original message: abcdefghijklmnopqrstuvwxyz
After memmove() without overlap: abcdqrstuvwxyzopqrstuvwxyz
After memmove() with overlap: abcdefefghijklmnqrstuvwxyz
blc-10-6{10}: uname -a
Linux blc-10-6 2.6.9-42.ELsmp #1 SMP Wed Jul 12 23:27:17 EDT 2006 i686 i686 i386 GNU/Linux
有人可以帮我理解memcpy对重叠内存区域的行为吗?
答案 0 :(得分:4)
具有重叠内存区域的函数memcpy()
的行为是未定义。实际观察到的结果可能取决于具体实现,在纯理论中,允许fly demons out of your nose。
实际上,memcpy
函数从一个缓冲区复制到另一个缓冲区,从低地址到高地址或反之亦然,可以优化复制字节数为2,4或8,导致各种各样的行为。
memcpy()
函数要求通过一个指针参数访问的地址和另一个从不相同的地址用C99标准中的简单英语表示:
如果在重叠的对象之间进行复制,则行为未定义。 (7.21.2.1:2)
此要求也由restrict
原型中的memcpy()
关键字汇总:
void *memcpy(void *restrict dest, const void *restrict src, size_t n);
虽然restrict
refers to the actual implementation, so it's not a self-contained description的确切含义。
specification language designed for this sort of thing中memcpy()
的要求规范如下:
/*@ requires valid_dst: \valid(((char*)dest)+(0..n - 1));
@ requires valid_src: \valid_read(((char*)src)+(0..n - 1));
@ requires \separated(((char *)dest)+(0..n-1),((char *)src)+(0..n-1));
@ assigns ((char*)dest)[0..n - 1] \from ((char*)src)[0..n-1];
...
@*/
void *memcpy(void *restrict dest, const void *restrict src, size_t n);
在memcpy()
的此合同中,\separated(((char *)dest)+(0..n-1),((char *)src)+(0..n-1))
要求确切地表达了哪些内存区域不应重叠。
如果您想要从内存区域到另一个您不确定它与第一个没有重叠的内存区域,请使用memmove()
。此函数采用与memcpy()
相同的参数,但即使存在重叠,也可以保证工作。