c - memcpy 在不同的编译器中给出不同的输出

标签 c gcc memcpy

请考虑以下程序

/* 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 thingmemcpy() 要求的规范看起来像这样:

/*@ 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/

相关文章:

更改函数内的矩阵并在 main 中使用它

c - Gcc 使用 sqrt 而不包含 math.h

mysql - 跨多个源文件的共享类型 - C

C结构共享公共(public)指针?

ios - iOS 如何处理低级 C/Objective-C 代码的内存损坏?

c - 程序因函数原型(prototype)声明而崩溃

c - 2D 和 3D 数组的动态分配/解除分配

c - 哪种代码更简洁、设计更好?

c - 消费消息时内存泄漏 - RabbitMQ C 库

gcc - 以非编译器特定的方式更改 Fortran 中的目录