我为以下 C 代码生成了未优化的代码:
#include<stdio.h>
int main(){
int i;
printf("%d\n", i);
}
生成的代码是:
.file "test.c"
.section .rodata
.LC0:
.string "%d\n"
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movl -4(%rbp), %eax
movl %eax, %esi
movl $.LC0, %edi
movl $0, %eax
call printf
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
.section .note.GNU-stack,"",@progbits
运行上面的汇编代码给出 0 作为输出。
我的问题是,变量 i
是如何初始化为 0
的?
最佳答案
NPE's answer告诉您 C 标准必须说明的关于这个程序的一切:行为是(完全)未定义的。
现在编译器已经生成了 Linux 上的 gcc -O0
未优化的 asm,我会尝试解释一下为什么您碰巧从该未优化的 asm 中得到零。
正如哈罗德指出的那样,它从堆栈内存中读取它没有写入的内容。 Linux 内核为您的进程提供其堆栈的零页,以避免信息泄漏。 (与 mmap(MAP_ANONYMOUS)
相同)
在调用 printf
之前,没有任何启动代码使用与 main
一样多的堆栈空间,因此从它加载的代码仍然会找到初始置零状态。
当然,printf
返回后,RSP下面的栈就脏了。动态链接是“延迟”完成的,所以它发生在 call printf
之后(实际上是 call printf@plt
如果你用 objdump 反汇编链接的目标文件-drwC -Mintel a.out
).这可能会使用一些堆栈空间进行临时存储。 (如果你曾经单步进入动态链接二进制文件中对库函数的第一次调用,你会在执行到达实际库函数代码之前看到大约 100 万条指令。[我忘记了我是如何测量这个数字的,但我似乎回想起来做的事情告诉我它可能有大约 1M 条指令。]) printf
本身肯定会压入/弹出一些寄存器,并且可能会使用堆栈上的一些其他暂存空间。
关于c - GCC 如何初始化自动变量?它们保证为 0 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41079465/