基本上,如果我有代码:
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'在这种情况下,语句只是在可执行文件中编译,主要是编译器评估条件?
答案 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()
中的代码。它会将if
和printf()
的代码生成到函数体中。
有几个原因它没有这样做。其中之一是功能的联系。它未声明为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返回,以便在运行时进行评估