我为以下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
?
答案 0 :(得分:3)
NPE's answer告诉你C标准关于这个程序的所有内容:行为是(完全)未定义的。
我将尝试解释为什么你碰巧从Linux上gcc -O0
的未优化asm中获得零,现在编译器已经生成了它。
正如哈罗指出的那样,它从堆栈内存中读取它没有写入。 Linux内核为其堆栈提供了归零页面,以避免信息泄漏。 (与mmap(MAP_ANONYMOUS)
相同)
在调用main
之前,没有任何启动代码使用尽可能多的printf
堆栈空间,因此来自它的负载仍会发现初始归零状态。
当然,在printf
返回后,RSP下面的堆栈将变脏。动态链接是“懒惰地”完成的,因此恰好在call printf
之后发生(如果您使用call printf@plt
反汇编链接的目标文件,则实际为objdump -drwC -Mintel a.out
)。这可能会使用一些堆栈空间进行临时存储。 (如果您单步进入动态链接二进制文件中对库函数的第一次调用,则在执行到达实际库函数代码之前,您将看到大约100万条指令。[我忘记了我是如何测量这个数字的,但我似乎回想做一些告诉我这可能是大约1M指令的东西。])printf
本身肯定会推送/弹出一些寄存器,并且可能在堆栈上使用其他一些临时空间。
答案 1 :(得分:2)
如何将变量初始化为0
不是。您的C代码有undefined behaviour,程序集反映了这一点。