编译器在哪里找到“printf”?
Posted
技术标签:
【中文标题】编译器在哪里找到“printf”?【英文标题】:Where the compiler find ``printf``? 【发布时间】:2016-05-11 05:37:25 【问题描述】:我已经有一段时间没有接触 c++了,这个问题可能很愚蠢,但它确实困扰了我一段时间。
假设我有以下 c 程序:
#include <stdio.h>
int main()
int i;
for(i=0; i<10; i++)
printf("Hello World!\n");
return 0;
我知道我包含stdio.h
的原因是因为我在main
中调用printf
,但我想知道编译器如何知道在编译期间在哪里可以找到printf()
的实现? stdio.h
只提供函数原型,但编译过程中到底发生了什么?
是否存在编译器知道的某些前缀路径来搜索printf
的实现?如果有,如何找到它们?
非常感谢!
【问题讨论】:
真正找到它的是链接器。是的,查找标准库的路径是预定义的。 @πάνταῥεῖ 你能再解释一下吗?谢谢! Here's some more info "链接阶段的路径查找" 对于不同的编译器,或者对于同一个编译器的不同版本是不同的。你为什么对它感兴趣? 如果您想真正了解编译器/链接器正在寻找头文件和库(至少对于 gcc)的幕后情况,请参阅 ***.com/a/11481258/12711。 【参考方案1】:如果您使用的是 linux 系统,则使用的 C 库可能是 glibc
。 GCC 实际上并不提供 C 库实现,只提供头文件。实际实现这些函数的定义是 C 库的工作。在 Linux 上,有一种叫做“共享库”的东西,由需要它的程序动态加载。例如:
ldd /usr/bin/gcc
linux-vdso.so.1 (0x00007ffd9e9f8000)
libm.so.6 => /lib64/libm.so.6 (0x00007ff9a35a6000)
libc.so.6 => /lib64/libc.so.6 (0x00007ff9a31de000)
/lib64/ld-linux-x86-64.so.2 (0x000055953c573000)
您可以通过传递-nostdlib
并在您自己的C 库中链接来禁用libc
的链接。还有其他方法可以替换 C 库提供的定义,例如链接到您自己的 malloc
等。链接器只允许为任何给定的声明找到 一个 定义,并且由于 C 中没有名称修饰,因此很容易做到。
这是一个过于简单的解释,并没有提到内置函数、数学库等。
【讨论】:
【参考方案2】:printf
是标准库的一部分,它被称为“标准”,因为它默认由 C 编译器链接。标准库通常由操作系统或编译器提供。在大多数 linux 系统上,它位于 libc.so
,而在 MS Windows C 库上,由 Visual C 运行时文件 msvcrt.dll
提供。
【讨论】:
【参考方案3】:经过一番挖掘,以下解释对我来说最有意义:
短版
头文件包含编译器需要的声明,但不包含实现。这 相应的库文件有那些。编译器需要头文件而不需要库;链接器 需要库,而不是头文件。
长版
头文件不包含printf()
的定义
函数(即它的实现。)包含在 C 标准 I/O 库中(即glibc
)。编译器无法创建机器指令
会导致
printf()
函数运行,因为它不知道在哪里
printf()
实施是;
它无法创建对此函数的调用。相反,编译器在可执行文件中放置一个符号
或多或少说,呼吁
printf()
必须由链接器解析。
链接器是在编译器之后运行的独立程序。它查看所有未解析的符号
程序,例如
printf()
,并尝试通过在软件库中查找它们的位置来解决它们
程序需要的。在这种情况下,链接器需要在 C 标准 I/O 库中查找位置
的
printf()
函数,以便它可以修补代码以调用它(ldd /usr/bin/gcc
方式或gcc -v foo.c -Wl --verbose
方式)。 C 标准 I/O 库
之所以特别,是因为它几乎用于每个 C 程序,因此许多 C 实现都包含它
在 C 运行时库中。这使得链接器可以轻松找到它
【讨论】:
以上是关于编译器在哪里找到“printf”?的主要内容,如果未能解决你的问题,请参考以下文章
如何在编译期间指示 Emscripten 应该在哪里找到源文件?
反编译flutter release apk。在哪里可以找到源代码?
在哪里可以找到带有 -static 选项的 macOS 的预编译 Qt?