编译器将printf更改为puts [英] Compiler changes printf to puts
问题描述
考虑以下代码:
#include <stdio.h>
void foo() {
printf("Hello world\n");
}
void bar() {
printf("Hello world");
}
这两个函数生成的程序集是:
The assembly produced by both these two functions is:
.LC0:
.string "Hello world"
foo():
mov edi, OFFSET FLAT:.LC0
jmp puts
bar():
mov edi, OFFSET FLAT:.LC0
xor eax, eax
jmp printf
现在,我知道了 puts与printf之间的区别,但是我发现这很有趣,因为gcc能够自省const char *并弄清楚是调用printf还是puts.
Now I know the difference between puts and printf, but I find this quite interesting that gcc is able to introspect the const char* and figure out whether to call printf or puts.
另一个有趣的事情是,在bar
中,编译器将返回寄存器(eax
)归零,即使它是void
函数.为什么要在那做而不是在foo
做?
Another interesting thing is that in bar
, compiler zero'ed out the return register (eax
) even though it is a void
function. Why did it do that there and not in foo
?
我是否假设编译器自检了我的字符串"是正确的,还是对此有另一种解释?
Am I correct in assuming that compiler 'introspected my string', or there is another explanation of this?
推荐答案
我是否假设编译器自检了我的字符串"是正确的,还是对此有另一种解释?
Am I correct in assuming that compiler 'introspected my string', or there is another explanation of this?
是的,这正是发生的情况.这是编译器完成的非常简单且常见的优化.
Yes, this is exactly what happens. It's a pretty simple and common optimization done by the compiler.
由于您的第一个printf()
通话是:
Since your first printf()
call is just:
printf("Hello world\n");
等效于:
puts("Hello world");
由于puts()
不需要扫描和解析用于格式说明符的字符串,因此它比printf()
快得多.编译器会注意到您的字符串以换行符结尾,并且不包含格式说明符,因此会自动转换该调用.
Since puts()
does not need to scan and parse the string for format specifiers, it's quite faster than printf()
. The compiler notices that your string ends with a newline and does not contain format specifiers, and therefore automatically converts the call.
这还节省了一些空间,因为现在只需要在生成的二进制文件中存储一个字符串"Hello world"
.
This also saves a bit of space, since now only one string "Hello world"
needs to be stored in the resulting binary.
请注意,通常对于以下形式的调用是不可能的:
Note that this is not possible in general for calls of the form:
printf(some_var);
如果some_var
不是简单的常量字符串,则编译器无法知道它是否以\n
结尾.
If some_var
is not a simple constant string, the compiler cannot know if it ends in \n
.
其他常见的优化方法是:
Other common optimizations are:
-
strlen("constant string")
可能会在编译时求值并转换为数字.
如果编译器确定 -
memmove(location1, location2, sz)
可能会转换为memcpy()
.
较小的 -
memcpy()
可以在一条mov
指令中转换,即使大小较大,有时也可以内联以更快地调用.
location1
和location2
不重叠,则strlen("constant string")
might get evaluated at compile time and converted into a number.memmove(location1, location2, sz)
might get transformed intomemcpy()
if the compiler is sure thatlocation1
andlocation2
don't overlap.memcpy()
of small sizes can be converted in a singlemov
instruction, and even if the size is larger the call can sometimes be inlined to be faster.
另一个有趣的事情是,在
bar
中,编译器将返回寄存器(eax
)归零,即使它是void
函数.为什么要在那做而不是在foo
做?
Another interesting thing is that in
bar
, compiler zero'ed out the return register (eax
) even though it is avoid
function. Why did it do that there and not infoo
?
请参阅此处:为什么在通话前将%eax归零来打印吗?
- 为什么GCC无法优化对printf的调用?
- 在C程序中,printf是否可以被puts自动替换?
- 为什么无论我使用printf还是puts,拆装时都显示puts?
- printf @ plt和puts @ plt之间的区别
- -O2将printf(%s \ n",str)优化为puts(str)
- Why doesn't GCC optimize this call to printf?
- Can printf get replaced by puts automatically in a C program?
- Why it shows puts when I disassemble no matter whether I'm using printf or puts?
- Difference between printf@plt and puts@plt
- -O2 optimizes printf("%s\n", str) to puts(str)
这篇关于编译器将printf更改为puts的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!