printf是否在C中使用堆栈帧?

时间:2017-06-19 09:18:09

标签: c stack printf

假设我们创建了一个用户函数 void func() func 的堆栈帧在函数调用时分配,只要在 main 等其他函数中调用它。

printf 相同吗? printf 也会占用堆栈帧吗?

4 个答案:

答案 0 :(得分:6)

前提:堆栈帧只是许多C实现的实现细节(具体来说,它通常在x86上使用);它既不是C标准所要求的东西(C标准不知道堆栈的东西),也不是所有实现都做的事情(甚至不是,它甚至可能取决于编译器标志/优化器考虑因素)。

现在,库函数当然最终只是已经编译到标准库中的常规函数​​,因此在这方面没有什么特别之处:在使用堆栈帧的实现中,每当执行函数调用时,被调用的函数集它的堆栈帧(除非它是内联的或者它是完全无关紧要的,但对于像printf这样的库函数通常不会发生。)

但是,没有什么可担心的 - 当函数返回时,新的堆栈帧被丢弃并且它的堆栈空间再次可用,所以你不必小心不要调用太多的函数。

答案 1 :(得分:2)

函数的堆栈帧由该函数保留;当然,没有其他函数可以知道另一个函数的堆栈使用需求。

因此,当您从func()调用main()时,func()内的代码(有时称为“前导码”)将保留该函数所需的堆栈空间。

是的,所有功能都适用,包括printf()

答案 2 :(得分:1)

这是一个带有main的小代码,可调用名为func的简单函数。

我们可以使用gcc main.c -S main.s编译此c代码。 main.s应为汇编输出。

在程序集中,您可能会看到CPU指令:push,pop,leave,enter。这些指令负责堆栈帧管理。

如果您使用gcc与其他选项进行编译,您可能会注意到一些堆栈管理差异。

#include <stdio.h>

int func(void);

int func(void)
{
    int i;

    for(i=0;i<100;i++)
        printf("%d",i);

    return i;
}

int main(void)
{
    return func();
}

GCC编译器程序集输出

        .file   "main.c"
        .section        .rodata
.LC0:
        .string "%d"
        .text
        .globl  func
        .type   func, @function
func:
.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    $0, -4(%rbp)
        jmp     .L2
.L3:
        movl    -4(%rbp), %eax
        movl    %eax, %esi
        movl    $.LC0, %edi
        movl    $0, %eax
        call    printf
        addl    $1, -4(%rbp)
.L2:
        cmpl    $99, -4(%rbp)
        jle     .L3
        movl    -4(%rbp), %eax
        leave
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE0:
        .size   func, .-func
        .globl  main
        .type   main, @function
main:
.LFB1:
        .cfi_startproc
        pushq   %rbp
        .cfi_def_cfa_offset 16
        .cfi_offset 6, -16
        movq    %rsp, %rbp
        .cfi_def_cfa_register 6
        call    func
        popq    %rbp
        .cfi_def_cfa 7, 8
        ret
        .cfi_endproc
.LFE1:
        .size   main, .-main
        .ident  "GCC: (Ubuntu 4.8.4-2ubuntu1~14.04.3) 4.8.4"
        .section        .note.GNU-stack,"",@progbits

这里是函数funcobjdump main.o -S)的objdump输出。 main.o获得者:gcc main.c -o main.o

000000000040052d <func>:
  40052d:   55                      push   %rbp
  40052e:   48 89 e5                mov    %rsp,%rbp
  400531:   48 83 ec 10             sub    $0x10,%rsp
  400535:   c7 45 fc 00 00 00 00    movl   $0x0,-0x4(%rbp)
  40053c:   eb 18                   jmp    400556 <func+0x29>
  40053e:   8b 45 fc                mov    -0x4(%rbp),%eax
  400541:   89 c6                   mov    %eax,%esi
  400543:   bf f4 05 40 00          mov    $0x4005f4,%edi
  400548:   b8 00 00 00 00          mov    $0x0,%eax
  40054d:   e8 be fe ff ff          callq  400410 <printf@plt>
  400552:   83 45 fc 01             addl   $0x1,-0x4(%rbp)
  400556:   83 7d fc 63             cmpl   $0x63,-0x4(%rbp)
  40055a:   7e e2                   jle    40053e <func+0x11>
  40055c:   8b 45 fc                mov    -0x4(%rbp),%eax
  40055f:   c9                      leaveq 
  400560:   c3                      retq   

0000000000400561 <main>:
  400561:   55                      push   %rbp
  400562:   48 89 e5                mov    %rsp,%rbp
  400565:   e8 c3 ff ff ff          callq  40052d <func>
  40056a:   5d                      pop    %rbp
  40056b:   c3                      retq   
  40056c:   0f 1f 40 00             nopl   0x0(%rax)

答案 3 :(得分:1)

取决于实施。所有标准都没有要求尾部函数调用来使用堆栈帧,但通常这些实现都会使用一个帧。

如此正确就是说

  用于func的

堆栈帧是通过函数调用分配的   在一些其他功能中调用,例如main   ONLY in some implementations