printf可以自动替换为C程序吗?

时间:2014-09-12 20:54:32

标签: c

#include <stdio.h>

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

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

此代码输出&#34; Hiya!&#34;什么时候跑。有人能解释一下原因吗?

编译行是: gcc main.c

编辑:它现在是纯粹的C,并且已经从编译行中删除了任何无关的东西。

4 个答案:

答案 0 :(得分:13)

是的,编译器可以通过对printf等效调用替换对puts的调用。

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

参考:N1570 7.1.3:

  

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

如果您删除自己的puts功能并检查汇编列表,则可能在生成的代码中找到puts的来电,您调用了printf在源代码中。 (我已经看到gcc执行了这个特殊的优化。)

答案 1 :(得分:8)

这取决于编译器和优化级别。最新版本的GCC,在某些常见系统上进行了一些优化,可以进行此类优化(将printf替换为puts,其中AFAIU是合法的,如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;您甚至可以通过gccgcc -fdump-tree-all -O泄漏批次的“转储”文件,这可能有助于您了解gcc正在做什么。

同样,此特定优化是有效且非常有用:任何libc的printf例程必须在运行时“解释” 打印格式字符串(处理%s等...特别);这实际上很慢。一个好的编译器可以在可能的情况下避免调用printf(并替换为puts)。

BTW 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 正确优化(clang也是如此)!

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

答案 2 :(得分:1)

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

的方式

有趣的解读是here

答案 3 :(得分:0)

据推测,你的图书馆的printf()调用了puts()。

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