[英]Compiler changes printf to puts
考虑以下代码:
#include <stdio.h>
void foo() {
printf("Hello world\n");
}
void bar() {
printf("Hello world");
}
这两个函数产生的程序集是:
.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 非常有趣。
另一个有趣的事情是,在bar
,编译器将返回寄存器 ( eax
) 清零,即使它是一个void
函数。 为什么它在那里而不是在foo
这样做?
我假设编译器“内省了我的字符串”是否正确,或者对此有另一种解释?
我假设编译器“内省了我的字符串”是否正确,或者对此有另一种解释?
是的,这正是发生的事情。 这是由编译器完成的非常简单且常见的优化。
由于您的第一个printf()
调用只是:
printf("Hello world\n");
它相当于:
puts("Hello world");
由于puts()
不需要扫描和解析字符串以获取格式说明符,因此它比printf()
快得多。 编译器注意到您的字符串以换行符结尾并且不包含格式说明符,因此会自动转换调用。
这也节省了一点空间,因为现在只需要在生成的二进制文件中存储一个字符串"Hello world"
。
请注意,对于以下形式的调用,这通常是不可能的:
printf(some_var);
如果some_var
不是简单的常量字符串,则编译器无法知道它是否以\\n
结尾。
其他常见的优化有:
strlen("constant string")
可能会在编译时被评估并转换为数字。location1
和location2
不重叠memmove(location1, location2, sz)
可能会转换为memcpy()
。memcpy()
可以在单个mov
指令中转换,即使尺寸更大,有时也可以内联调用以更快。另一个有趣的事情是,在
bar
,编译器将返回寄存器 (eax
) 清零,即使它是一个void
函数。 为什么它在那里而不是在foo
这样做?
请参阅此处: 为什么 %eax 在调用 printf 之前归零?
另一个有趣的事情是,在 bar 中,编译器将返回寄存器 (eax) 清零,即使它是一个 void 函数。 为什么它在那里而不是在 foo 中这样做?
这与标题中的问题完全无关,但仍然很有趣。
异或归零%eax
在调用 printf之前,因此是调用的一部分,与返回值无关。 发生这种情况的原因是printf
是一个可变参数函数,并且可变参数函数的 x86_64 ABI 需要在 xmm 寄存器中传递浮点参数,并且需要在 %al 中传递此类参数的数量。 所以这条指令是为了确保 %al 为 0,因为没有参数在 xmm 寄存器中传递给 printf。
puts 不是可变参数函数,因此不需要它。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.