#include 指令将导致在编译之前将头文件的内容放在源代码中:例如,如果我包含 stdio.h ,则预处理器会将 stdio.h 的所有内容放在源代码中,然后编译不是吗?
所以让我们假设我只是在我的代码中使用 printf() 函数。所以在编译之后和链接之间肯定会发生一些事情,这将删除头文件中包含的所有函数实现,并且只在可执行文件的导入表中插入 printf() 函数实现,知道所有其他函数是用源码编译的。你能给我解释一下吗?
该printf
函数实际上并不在头文件中,它位于由链接器自动链接到可执行文件的库中。
头文件应该只包含函数原型,即函数的声明而不是定义。由于头文件中没有函数定义,因此实际上不会为它们生成任何代码,并且编译器将确保只有您实际调用的函数才能在生成的目标文件中获得一个条目,以便链接器知道它。
在 C 头文件中声明的函数原型充当编译时约束。它识别正确数量的参数和这些参数的正确类型。这种约束的验证发生在编译阶段(即*.c -> *.o
)。
(静态)链接阶段基于每个对象文件从编程库中消除未使用的二进制对象。编程库是二进制对象文件(* .o)的存档,每个文件都包含一组函数、常量等的实现。至于你的问题,二进制对象文件包含printf
(以及其他所有内容)的实现目标文件)将链接到您的程序中。其他未使用的目标文件将作为链接时优化被淘汰。
头文件只包含一个声明,例如格式
size_t printf(char const *format, ...);
这告诉编译器如果它遇到这个词printf
并且它被用作一个函数,那么它可以生成一个函数调用。
call 指令包含函数实际地址的占位符,然后在构建最终可执行文件时由链接器插入。
链接器通常只保留一个未解析符号的运行列表,并在遇到这些占位符之一时添加到此列表中,并在找到符号定义时填写实际地址。在 的情况下printf
,该定义位于 C 标准库中,因此链接器确保库在运行时加载,并且调用指令指向正确的地址。