【问题标题】:Can printf get replaced by puts automatically in a C program?printf 可以在 C 程序中自动替换为 puts 吗?
【发布时间】:2014-09-12 20:54:31
【问题描述】:
#include <stdio.h>

int puts(const char* str)
{
    return printf("Hiya!\n");
}

int main()
{
    printf("Hello world.\n");
    return 0;
}

此代码输出“嗨!”运行时。有人能解释一下原因吗?

编译行是: gcc main.c

编辑:它现在是纯 C,所有无关的东西都已从编译行中删除。

【问题讨论】:

  • 不依赖GCC优化选项的It优化。
  • 这个问题被标记为c,尽管编译器调用表明代码被编译为C++。
  • 是的,gcc 可以优化从printf()puts() 的简单常量字符串打印,因为后者性能更好......因为您将标准库 puts() 替换为本地符号,即就是你得到的。如果你使用 -O0 而不是 -O2,你实际上应该调用 printf()
  • 好的,重新标记和编辑标题。
  • 顺便说一句,您的代码看起来像 C 代码,但您将其编译为 C++11;你为什么这样做?如果你想用 C++11 编写代码,请使用 std::cout&lt;&lt;"hello world" &lt;&lt; std::endl;

标签: c


【解决方案1】:

是的,编译器可能会将对printf 的调用替换为对puts等效调用。

因为您定义了自己的函数puts,与标准库函数同名,所以您的程序的行为是未定义的。

参考:N15707.1.3:

以下任何子条款中具有外部链接的所有标识符[这包括puts]始终保留用作具有外部链接的标识符。
...
如果程序在一个 它被保留的上下文(除了 7.1.4 允许的),或定义一个保留的 标识符作为宏名称,行为未定义。

如果您删除自己的puts 函数并检查程序集列表,您可能在生成的代码中找到对puts 的调用,而您在源代码中调用了printf。 (我已经看到 gcc 执行了这种特殊的优化。)

【讨论】:

  • (免责声明:仅在托管实现中)
【解决方案2】:

这取决于编译器和优化级别。 GCC 的最新版本,在一些通用系统上,经过一些优化,能够进行这样的优化(用 puts 替换简单的 printf,AFAIU 是合法的 w.r.t. 标准,如 C99)

您应该在编译时启用警告(例如,首先尝试使用gcc -Wall -g 进行编译,然后使用gdb 进行调试,然后当您对您的代码有信心时使用gcc -Wall -O2 进行编译)

顺便说一句,重新定义 puts 真的很难看,除非你是故意这样做的(即编写你自己的 C 库,然后你必须遵守标准)。您会收到一些undefined behavior(另请参阅this answer,了解 UB 的可能后果)。实际上,您应该避免重新定义标准中提到的名称,除非您真的很清楚自己在做什么以及编译器内部发生了什么。

另外,如果您使用像 gcc -Wall -static -O main.c -o yourprog 这样的静态链接进行编译,我敢打赌链接器会抱怨(关于 puts 的多个定义)。

但是 IMNSHO 你的代码是完全错误的,你知道的。

此外,您可以编译以获取汇编程序,例如与gcc -fverbose-asm -O -S;您甚至可以要求gcc 溢出很多 的“转储”文件,gcc -fdump-tree-all -O 这可能有助于您了解gcc 在做什么。

再一次,这种特殊的优化有效并且非常有用:任何 libc 的 printf 例程都必须在运行时“解释”打印格式字符串(特别处理%s等...);这在实践中相当缓慢。一个好的编译器在可能的情况下避免调用printf(并替换为puts)是正确的。

顺便说一句,gcc 并不是唯一进行这种优化的编译器。 clang 也可以。

另外,如果你使用编译

gcc -ffreestanding -O2 almo.c -o almo

almo 程序显示Hello world.


如果您想要另一个花哨和令人惊讶的优化,请尝试编译

// file bas.c
#include <stdlib.h>
int f (int x, int y) {
  int r;
  int* p = malloc(2*sizeof(int));
  p[0] = x;
  p[1] = y;
  r = p[0]+p[1];
  free (p);
  return r;
}   

使用gcc -O2 -fverbose-asm -S bas.c 然后查看bas.s;你不会看到任何对mallocfree 的调用(实际上,没有发出call 机器指令),而且gcc正确 进行优化的(@ 也是如此) 987654352@)!

PS:Gnu/Linux/Debian/Sid/x86-64; gcc 是 4.9.1 版,clang 是 3.4.2 版

【讨论】:

  • 好点;刚刚尝试使用 gcc,所以问题是存在的,感谢您的回答。稍后将进行更多调查并接受答案。 :)
  • 我知道这是错的。我很好奇它为什么会这样。
  • 哇,这太酷了(花哨且令人惊讶的优化)
【解决方案3】:

在您的可执行文件上尝试ltrace。您将看到 printf 被编译器调用的 puts 替换。这取决于你调用printf的方式

一个有趣的阅读是here

【讨论】:

    【解决方案4】:

    大概你的库的 printf() 调用 puts()。

    您的 puts() 正在替换库版本。

    【讨论】:

    • 其实printf没有runtime调用puts,但是compiler是正确的在 compile-timeprintf 替换为 puts
    猜你喜欢
    • 1970-01-01
    • 2015-01-06
    • 1970-01-01
    • 2014-07-03
    • 1970-01-01
    • 2012-03-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多