编译器将printf更改为puts [英] Compiler changes printf to puts

查看:310
本文介绍了编译器将printf更改为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")可能会在编译时求值并转换为数字.
  • 如果编译器确定location1location2不重叠,则
  • memmove(location1, location2, sz)可能会转换为memcpy().
  • 较小的
  • memcpy()可以在一条mov指令中转换,即使大小较大,有时也可以内联以更快地调用.
  • strlen("constant string") might get evaluated at compile time and converted into a number.
  • memmove(location1, location2, sz) might get transformed into memcpy() if the compiler is sure that location1 and location2 don't overlap.
  • memcpy() of small sizes can be converted in a single mov 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 a void function. Why did it do that there and not in foo?

请参阅此处:为什么在通话前将%eax归零来打印吗?

  • 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆