如何在GCC中初始化自动变量?他们保证是0吗?

时间:2016-12-10 19:34:58

标签: c gcc assembly x86

我为以下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

2 个答案:

答案 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,程序集反映了这一点。

Herehere是关于Linux上进程启动状态的一些旧信息。它有些相关,但不会使有关未定义行为的要点失效。)