是否在运行时预编译或评估没有变量的C函数调用?

时间:2015-03-16 12:10:33

标签: c printf conditional

基本上,如果我有代码:

void main(void){
   foo(1,3);
}

foo在哪里:

void foo(int x, int y){
   if(x==0) return;
   else if (x==1){
      if(y==0) printf("hello, world");
      else if (y==2) printf("goodbye.");
      else if (y==3) printf("no.");
      else return;
   }
   else return;
}

条件句(假设它们适用)是否会在运行时进行评估,或者是否会打印出来?' printf'在这种情况下,语句只是在可执行文件中编译,主要是编译器评估条件?

3 个答案:

答案 0 :(得分:2)

  

是否会在运行时评估条件(假设它们适用),   或者' printf'在这种情况下的语句只是编译在   可执行文件,主要是编译器评估条件?

编译器可以自由发出它想要的任何代码,只要语义保持相同引用。大多数编译器都具有可配置的优化级别,可以控制它们在转换源代码方面的积极程度。如果是gcc,则相关标记为-O x

查看代码发出的唯一方法是自己检查。如果是gcc,您可以使用-S标志,输出生成的汇编程序。

在您的计划中,gcc -O0 -S opt.c(无优化)产生以下内容:

main:
.LFB1:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    movl    $3, %esi
    movl    $1, %edi
    call    foo #        <---
    popq    %rbp
    .cfi_def_cfa 7, 8
    ret
    .cfi_endproc

gcc -O1 -S opt.c及更高优化级别导致:

.LC2:
    .string "no."
(...)
main:
.LFB12:
    .cfi_startproc
    subq    $8, %rsp
    .cfi_def_cfa_offset 16
    movl    $.LC2, %edi
    movl    $0, %eax
    call    printf  #    <----
    addq    $8, %rsp
    .cfi_def_cfa_offset 8
    ret
    .cfi_endproc

答案 1 :(得分:1)

编译器无法解释函数foo()中的代码。它会将ifprintf()的代码生成到函数体中。

有几个原因它没有这样做。其中之一是功能的联系。它未声明为static,这意味着它可能在其他.c文件中使用;编译器不能只猜测它在实际调用中的参数值是什么。

用不同的参数调用它来输出不同的东西是你首先编写函数的原因。

根据您在调用时使用的编译器和优化开关,它可以内联对foo(1,3)的调用。内联意味着编译器用函数体的代码替换对函数的调用。在这种情况下,它可以优化内联代码,因为它知道参数的值,它可以告诉哪些printf()运行;它删除了if和其他printf(),因为它们是死代码而不是对foo(1,3)的调用,而是生成printf("no.");的代码。但这只会因为函数调用的参数是常量(即它们在编译时已知)而发生。

但是,即使在这种情况下,仍会生成函数代码。 如果调用foo(1,3);是对该函数的唯一调用,并且编译器能够内联它,则链接器将删除函数的代码(将被忽略,因为它不被调用)它会生成最终的可执行文件。

检查编译器的命令行开关以获取优化标志。还要检查如何指示它生成一个程序集文件(带注释)以查看它生成的代码(如果它内联调用foo(1,3),你可以看到它。)

答案 2 :(得分:0)

在反汇编foo函数之后:

011714AE  push        3  
011714B0  push        1  
011714B2  call        foo (11711D6h)

这意味着C函数首先将变量推送到内存中,然后从esp返回,以便在运行时进行评估