c - GCC 如何初始化自动变量?它们保证为 0 吗?

标签 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 的?

最佳答案

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/

相关文章:

c - x86 add 和 addl 操作数相加错误?

assembly - FRDM-KL25z 组装延迟循环导致复位

c - 如何使用 C 中数组中的元素查找和替换文本文件中的位置?

c macOS 中的段错误;有时,有时不

iphone - 将 Xcode 4.2 中的默认编译器更改为 GCC 4.2 有什么问题吗?

C++ 在 Windows 上编译但在 OS X GCC 下出错

c++ - 按照每个通用寄存器的用途对 x86 程序集进行编码是否有必要或更容易

c - 如何使用 mbedtls_pk_verify 验证签名

c - C中删除字符

c++ - 虚拟基类函数中派生类的大小