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

#include  int puts(const char* str) { return printf("Hiya!\n"); } int main() { printf("Hello world.\n"); return 0; } 

此代码输出“Hiya!” 什么时候跑。 有人可以解释原因吗?

编译行是: gcc main.c

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

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

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

参考: N1570 7.1.3:

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

如果删除自己的puts函数并检查汇编列表,则可能会在生成的代码中找到调用printf ,在源代码中调用printf 。 (我见过gcc执行这个特殊的优化。)

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

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

顺便说一句,重新定义puts真的很难看,除非你故意这样做(即编写自己的C库,然后你必须服从标准)。 您正在获得一些未定义的行为 (另请参阅此答案,了解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 )。

BTW gcc不是唯一进行优化的编译器。 clang也做到了。

另外,如果您使用编译

 gcc -ffreestanding -O2 almo.c -o almo 

almo程序显示Hello world.


如果你想要另一种花哨和惊人的优化,请尝试编译

 // file bas.c #include  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 ; 你不会看到对malloc任何调用或free (实际上,没有发出call机器指令),再次, gcc正确的优化( clang也是如此)!

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

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

这里有一个有趣的解读

据推测,你的库的printf()调用puts()。

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