关在C叠memcopying数据
我组建了一个C
谜语一对夫妇的朋友时,一个朋友把我的注意力的事实,下面的代码片段(这恰好是我一直在写谜语的一部分)编译并在OSX关在C叠memcopying数据
#include <stdio.h>
#include <string.h>
int main()
{
int a = 10;
volatile int b = 20;
volatile int c = 30;
int data[3];
memcpy(&data, &a, sizeof(data));
printf("%d %d %d\n", data[0], data[1], data[2]);
}
运行时遇到不同的你所期望的输出是什么是10 20 30
,这恰好是这种情况Linux
下,但是当代码OSX
下建造的,你会获得10
,随后是两个随机数。经过一些调试并查看编译器生成的assembly
后,我得出结论,这是由于堆栈是如何构建的。我决不是一个assembly
专家,但Linux
生成的汇编代码看起来非常简单易懂,而在OSX
产生的一个扔我一点点。也许我可以从这里使用一些帮助。
这是已在Linux
生成的代码:
.file "code.c"
.section .text.unlikely,"ax",@progbits
.LCOLDB0:
.section .text.startup,"ax",@progbits
.LHOTB0:
.p2align 4,,15
.globl main
.type main, @function
main:
.LFB23:
.cfi_startproc
movl $10, -12(%rsp)
xorl %eax, %eax
movl $20, -8(%rsp)
movl $30, -4(%rsp)
ret
.cfi_endproc
.LFE23:
.size main, .-main
.section .text.unlikely
.LCOLDE0:
.section .text.startup
.LHOTE0:
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.4) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
这是已在OSX
生成的代码:我真的只在这里有两个问题感兴趣
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 12
.globl _main
.p2align 4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
subq $16, %rsp
movl $20, -8(%rbp)
movl $30, -4(%rbp)
leaq L_.str(%rip), %rdi
movl $10, %esi
xorl %eax, %eax
callq _printf
xorl %eax, %eax
addq $16, %rsp
popq %rbp
retq
.cfi_endproc
.section __TEXT,__cstring,cstring_literals
L_.str: ## @.str
.asciz "%d %d %d\n"
.subsections_via_symbols
。
这究竟是为什么?
是否有任何GET-变通这个问题?
我知道这是不是利用堆栈,因为我是一个专业的C语言开发,这是真的,我发现这个问题有趣的一些我的时间投入到唯一理由一条可行之路。
访问存储过去的一个声明的变量的到底是不确定的行为 - 没有保证的,当你尝试这样做,会发生什么。由于编译器是如何在Linux下生成程序集的,因此您恰好在堆栈中直接获取3个变量,但这种行为只是巧合 - 编译器可以合法地在堆栈中的变量之间添加额外的数据,或者真的做任何事 - 结果不是由语言标准定义的。因此,在回答你的第一个问题时,这是因为你所做的不是按照设计语言的一部分。在回答第二个问题时,没有办法可靠地从多个编译器获得相同的结果,因为编译器未编程为可靠地重现未定义的行为。
那么,在这种情况下,gcc被迫将三个值存储在内存中,因为'b'和'c'是易失性的,'a'通过引用'memcpy'传递。虽然你的答案的其余部分是有道理的 –
@ FadiHannaAL-Kass是真的,错过了那个细节 - 编辑 –
@ FadiHannaAL-Kass只要声明它'volatile'还不够。任何智能编译器都会丢弃“volatile”,因为不会采用地址。 –
未定义行为。你不希望复制10,20,30。你希望不要seg-fault。
没有什么可以保证a,b和c是顺序存储器地址,这是你的天真假设。在Linux上,编译器碰巧使它们连续。你甚至不能依靠gcc总是这样做。
你已经知道的行为是不确定的。一个很好的理由的行为是在OS/X 不同和Linux是这些系统使用不同的编译器,生成不同的代码:
当您运行在Linux中
gcc
,你调用安装版Gnu C编译器。当您在您的OS/X版本中运行
gcc
时,很可能会调用已安装的clang
版本。
尝试gcc --version
在两个系统上,并惊讶你的朋友。
您正在调用未定义的行为。所有投注都关闭。就个人而言,我不希望输出“10 20 30”。相反,我期望'1͉̟̗̗͕͊̅̋̇̽̊̔͠0̶̨̳̖̰̥͍̞͛̇̒̌̋̏̚͠2̶̨̼̰͖̬̒̓͟͞͡͞0̳͔̩̰̼̙͓͓̗͌̊̔͐̌̚͠3̗͔̰̥͉͎̦̦͋͊̓͛͢͜Ȟ̳̖͙͓̻͉̈͊̑̓͢͞͝Ę̛̯̣̞̰͉̗͐̑̍̓̔Ç̵͔̝͉͇͖̾͆̐̐̽̓̕͘͜Ỏ̵̰̣̳̤̪̅̄̌̈̐̅͌̚͞M͖̪̝͈͇͙͛̽͊̈̔͝Ę̘̺̙͇̦̖̯̜̿̊̅̔̑Ş̵̡̢̳̔̾̅̽̕̕͜͝' – Cornstalks
这种未定义的行为如何?我给'memcpy'开始一个内存地址,读取的字节数为 –
@ FadiHannaAL-Kass - 在'a'的地址处只有一个元素,但是您正在读取3个元素('b'和'在某些平台上)。这是未定义的行为。正如你所见,'a','b'和'c'可能不是连续的。另请参阅[是否未定义的行为来访问阵列超出其最终,如果该区域分配?](https://stackoverflow.com/q/12354413/608639) – jww