#include <stdio.h>
int puts(const char* str)
{
return printf("Hiya!\n");
}
int main()
{
printf("Hello world.\n");
return 0;
}
Run Code Online (Sandbox Code Playgroud)
此代码输出"Hiya!" 什么时候跑.有人能解释为什么吗?
编译行是:
gcc main.c
编辑:它现在是纯C,任何无关的东西都已从编译行中删除.
Kei*_*son 14
是的,编译器可以printf
通过等效调用来替换对的调用puts
.
因为您puts
使用与标准库函数相同的名称定义了自己的函数,所以程序的行为是未定义的.
参考:N1570 7.1.3:
在以下任何子条款[包括
puts
]中具有外部链接的所有标识符始终保留用作具有外部链接的标识符.
...
如果程序在保留它的上下文中声明或定义标识符(除了7.1.4允许的标识符),或者将保留标识符定义为宏名称,则行为是未定义的.
如果删除自己的puts
函数并检查程序集列表,则可能会puts
在生成的代码中找到printf
对源代码中调用的调用.(我见过gcc执行这个特殊的优化.)
这取决于编译器和优化级别.最新版本的GCC,在某些常见系统上,经过一些优化,能够进行这样的优化(取代简单printf
的puts
,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
正在做什么.
同样,这个特殊的优化是有效的,非常有用:printf
任何libc 的例程都必须在运行时 "解释" 打印格式字符串(处理%s
等...特别); 这实际上很慢.一个好的编译器正确地避免在可能的情况下调用printf
(和替换puts
).
BTW gcc
并不是唯一进行优化的编译器.clang
也做到了.
另外,如果您使用编译
gcc -ffreestanding -O2 almo.c -o almo
Run Code Online (Sandbox Code Playgroud)
该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;
}
Run Code Online (Sandbox Code Playgroud)
与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