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

Alm*_*lmo 13 c

#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执行这个特殊的优化.)

  • (免责声明:仅限托管实施) (2认同)

Bas*_*tch 8

这取决于编译器和优化级别.最新版本的GCC,在某些常见系统上,经过一些优化,能够进行这样的优化(取代简单printfputs,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; 你不会看到任何电话mallocfree(实际上,没有call机器指令发出),并再次,gcc正确的优化(也是如此clang)!

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