【问题标题】:Compiler changes printf to puts编译器将 printf 更改为 puts
【发布时间】:2021-12-01 02:31:14
【问题描述】:

考虑以下代码:

#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 and printf 之间的区别了,但我发现 gcc 能够自省 const char* 并确定是调用 printf 还是 puts,这很有趣。

另一个有趣的事情是,在bar 中,编译器将返回寄存器 (eax) 清零,即使它是一个 void 函数。为什么它在那里而不是在foo 中这样做?

我是否正确假设编译器“内省了我的字符串”,或者对此有另一种解释?

【问题讨论】:

  • 顺便说一句:一个好的编译器确实会检查格式字符串,因为它会验证所提供参数的数量和类型,并在不匹配时发出警告。
  • @Gerhardh 编译器需要检查字符串是否存在换行符。
  • @Gerhardh puts 在字符串之后输出一个换行符,因此如果没有,则无法替换。
  • @Gerhard puts 不检查任何东西:它输出你给它的东西,但还有另一个运行时差异。如果我将NULL 指针作为字符串传递给printf,MSVC 会友好地(但不是必须)输出(null),但使用puts 它会崩溃。
  • 这不是对字符串的自省;编译器没有调查自己。这是普通的旧检查。

标签: c gcc assembly compiler-optimization


【解决方案1】:

我是否正确假设编译器“内省了我的字符串”,或者对此有另一种解释?

是的,这正是发生的事情。这是编译器完成的一个非常简单和常见的优化。

因为您的第一个printf() 电话只是:

printf("Hello world\n");

相当于:

puts("Hello world");

由于puts() 不需要扫描和解析格式说明符的字符串,它比printf() 快得多。编译器注意到您的字符串以换行符结尾并且不包含格式说明符,因此会自动转换调用。

这也节省了一点空间,因为现在只需要在生成的二进制文件中存储一个字符串 "Hello world"

请注意,这通常不适用于以下形式的调用:

printf(some_var);

如果some_var不是一个简单的常量字符串,编译器无法知道它是否以\n结尾。

其他常见的优化有:

  • strlen("constant string") 可能会在编译时进行评估并转换为数字。
  • 如果编译器确定 location1location2 不重叠,memmove(location1, location2, sz) 可能会转换为 memcpy()
  • 小尺寸的 memcpy() 可以在单个 mov 指令中转换,即使尺寸较大,有时也可以内联调用以更快。

另一个有趣的事情是,在bar 中,编译器将返回寄存器 (eax) 清零,即使它是一个 void 函数。为什么它在那里而不是在foo 中这样做?

请看这里:Why is %eax zeroed before a call to printf?


相关有趣的帖子

【讨论】:

【解决方案2】:

另一个有趣的事情是,在 bar 中,编译器将返回寄存器 (eax) 清零,即使它是一个 void 函数。为什么它在那里而不是在 foo 中这样做?

这与标题中的问题完全无关,但仍然很有趣。

xor 归零%eax调用 printf 所以是调用的一部分并且与返回值无关。发生这种情况的原因是printf 是一个可变参数函数,而用于可变参数函数的 x86_64 ABI 需要在 xmm 寄存器中传递浮点参数,并且需要在 %al 中传递此类参数的数量。所以这条指令是为了确保 %al 为 0,因为没有参数在 xmm 寄存器中传递给 printf。

puts 不是 varargs 函数,所以这里不需要它。

【讨论】:

    猜你喜欢
    • 2021-11-23
    • 1970-01-01
    • 1970-01-01
    • 2016-07-20
    • 1970-01-01
    • 2021-02-22
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多