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

没有变量的C函数调用是否在运行时预编译或计算?,c,printf,conditional,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;

基本上,如果我有代码:

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”语句只是在可执行文件中编译,基本上由编译器计算条件?

编译器无法解释函数
foo()
中的代码。它将在函数体中生成
if
s和
printf()
s的代码

它没有做到这一点有几个原因。其中之一是功能的链接。它没有声明为
静态
,这意味着它可以在其他
.c
文件中使用;编译器不能仅仅猜测实际调用中参数的值

使用不同的参数调用它以输出不同的内容,这就是您首先编写函数的原因

根据调用时使用的编译器和优化开关,它可以内联调用
foo(1,3)
。内联意味着编译器用函数体的代码替换对函数的调用。在这种情况下,它可以优化内联代码,因为它知道参数的值,并且可以判断哪个
printf()
运行;它删除
if
s和另一个
printf()
s,因为它们是死代码,而不是调用
foo(1,3)
它生成
printf(“no.”)的代码。但这只能是因为函数调用的参数是常量(即,它们在编译时是已知的)

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


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

反汇编后,foo函数如下所示:

011714AE  push        3  
011714B0  push        1  
011714B2  call        foo (11711D6h)
这意味着C函数首先将变量推入内存,然后从esp返回以在运行时进行求值

条件(假设它们适用)是否会在运行时进行计算, 或者本例中的“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

void main(void)
是错误的,它是
int main(void)
如果您的编译器没有告诉您它是错误的,那么您可能需要配置编译器警告或更改编译器。这显然是一个小问题,我在问,因为我正在处理一个实时性能优先的嵌入式系统,这取决于编译器,编译器设置,可能是两个函数是否在同一个编译单元中(或者如果您有PGO)。。。C标准允许编译器进行优化,但本质上没有一个是强制性的。如果你让编译器优化代码,那么它将得到优化,但无论如何你应该使用
开关
,不是吗?@iharob,因为我的评论没有回复你:对于这个应用程序,代码运行在带有JeOS的嵌入式系统上,我不在乎main是否/何时返回,如果返回又会是什么,因为我的代码将始终从“main()”的顶部重新开始运行。这肯定是特定于实现的,取决于编译器、操作系统、优化标志。原因取决于编译器,但我确信所有编译器的操作都会给出相同的结果。如果您不更改代码,它将在运行时进行计算。
.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