c - 为什么编译器要在栈上腾出空间

标签 c linux assembly x86-64

我不知道我的标题是否合适,因为我的问题是:我知道有时(例如当我想使用 argv[] 时)编译器必须在堆栈上为命令行参数安排空间。现在我写了一个简单的程序只是为了看看 C 编译器如何处理简单的 C 数组(实际上它处理它们的方式与 std::array's 相同)。我正在使用 Manjaro Linux 64 位。 C 代码如下所示:

#include <stdio.h>

int main(){
    int a[5] = {1,2,3,4,5};
    //printf("%d", a[1]);
    return 0;
}

程序集生成的输出(来自 gcc main.c -fno-asynchronous-unwind-tables -o XD.s -S):

    .file   "main.c"
    .text
    .globl  main
    .type   main, @function
main:
    pushq   %rbp
    movq    %rsp, %rbp
    movl    $1, -32(%rbp)
    movl    $2, -28(%rbp)
    movl    $3, -24(%rbp)
    movl    $4, -20(%rbp)
    movl    $5, -16(%rbp)
    movl    $0, %eax
    popq    %rbp
    ret
    .size   main, .-main
    .ident  "GCC: (GNU) 6.3.1 20170109"
    .section    .note.GNU-stack,"",@progbits

现在,当我取消注释 printf 语句时,代码如下所示:

    .file   "main.c"
    .section    .rodata
.LC0:
    .string "%d"
    .text
    .globl  main
    .type   main, @function
main:
    pushq   %rbp
    movq    %rsp, %rbp
    subq    $32, %rsp
    movl    $1, -32(%rbp)
    movl    $2, -28(%rbp)
    movl    $3, -24(%rbp)
    movl    $4, -20(%rbp)
    movl    $5, -16(%rbp)
    movl    -28(%rbp), %eax
    movl    %eax, %esi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    movl    $0, %eax
    leave
    ret
    .size   main, .-main
    .ident  "GCC: (GNU) 6.3.1 20170109"
    .section    .note.GNU-stack,"",@progbits

中间部分很明显,就是调用了printf。但是为什么编译器要在此处放置 subq $32, %rsp 行?为什么它没有出现在第一个示例中,没有 printf 语句?

最佳答案

这是您的编译器所做的优化。它在第一种情况下意识到 main 是一个叶函数,因此它知道数组在堆栈上是安全的。而在第二种情况下,对 printf 的调用将覆盖堆栈帧,因此它通过递增 %rsp 来保护堆栈帧。

实际上编译器有很多这样的优化。例如,ABI 指定堆栈在调用之前必须是 16 字节对齐的,并且以 %rsp 是 16 字节对齐的方式创建的帧。但是如果该函数不调用任何其他函数或不使用任何 SSE 指令(一个需要对齐堆栈帧的指令示例),它就会违反 ABI 要求。 这些实际上只是为了尽可能节省每个字节而进行的微优化。

关于c - 为什么编译器要在栈上腾出空间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42482114/

相关文章:

c - 为什么我从 clock() 获得的值与实时不同步?

assembly - 如何在像素屏幕中写入像素?

c - 最快的位计数方法

linux - bash命令从文件中删除两个 block 之间的内容

c - 为什么会出现此警告( no return ,函数返回非 void )?

assembly - 为什么在 Pentium IA-32 上不能将一个字节压入堆栈?

c - 使用 C 中的相同函数从不同结构中获取相同字段

c - errno、strerror 和 Linux 系统调用

c - lapack dgels_ 段错误 11

python - 为什么发送连续的 UDP 消息会导致消息延迟到达?