请考虑以下程序
/* 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);
}
现在考虑 2 个不同编译器的输出
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 对于内存重叠区域的行为吗?
最佳答案
内存重叠区域的函数memcpy()
的行为是未定义。实际观察到的结果可能取决于具体的实现,在纯理论中,允许 fly demons out of your nose .
实际上,memcpy
函数从一个缓冲区复制到另一个缓冲区,或者从低地址复制到高地址,或者相反,并且可以优化为复制 2、4 或 8 个字节,导致各种各样的行为。
memcpy()
函数要求通过一个指针参数访问的地址与另一个指针参数访问的地址永远不会相同,这在 C99 标准中用简单的英语表达:
If copying takes place between objects that overlap, the behavior is undefined. (7.21.2.1:2)
此要求也通过 memcpy()
原型(prototype)中的 restrict
关键字进行了总结:
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() 相同的参数,但即使存在重叠也能保证正常工作。
关于c - memcpy 在不同的编译器中给出不同的输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24148788/