我最近读了CSAPP,对它的编译系统部分有些怀疑。
现在我们有一个使用HelloWorld.c的示例(只打印hello world),书上说在Pre-processor阶段,他们用这个头文件的内容替换了“#include“行,但是当我打开stdio. h时,发现只有printf()的声明,没有具体实现,那么在编译系统中,什么时候会引入printf()的具体实现呢?
书中还提到,在链接阶段,链接器(ld)链接了helloworld.o和printf.o,为什么链接器知道把我的目标文件链接到printf.o?在编译系统中,为什么在第一步(预处理器阶段)声明这个函数,在最后一步(链接阶段)链接具体实现?
5条答案
按热度按时间fae0ux8s1#
实际上,过于简化:
.a
或unix上的.so
文件)。libc.so
有一个printf
函数,该函数在库文件libc.so
中的字符号0xaabbccdd
处开始。printf
需要什么参数。它需要int
吗?它需要char *
吗?它需要uint_least64_t
吗?它在头文件-int printf(const char *, ...);
中。头文件告诉编译器如何调用函数(函数需要什么参数以及返回什么类型)。注意每个.c
文件都是单独编译的。printf
)和编译的函数体。头文件具有int printf(const char *, ...);
,但没有函数体。push pointer to "%d\n" on the stack; push some int on the stack; call printf; pop from the stack the returned "int"; rest of the instructions;
的程序集。call printf
,然后说:“哦,你的代码中没有printf
主体”。所以然后它在库中搜索printf
,链接器遍历所有你链接程序的库,然后在标准库中找到printf
,它在libc.so
中,地址是0xaabbccdd
。因此链接器用call printf
替换goto libs.so file to address 0xaabbccdd
类型的指令。call printf
将跳转到libc.so
文件的指定位置。我上面所写的只是为了说明。
oaxa6hgo2#
为什么链接器知道将我的目标文件链接到printf.o
因为编译器会在它生成的文件中记录下这一点,这些文件通常称为对象文件(.o)。
为什么它在第一步就声明了这个函数。
去了解它。
......并在最后一步中链接具体实施
因为没有必要早点这么做。
t2a7ltrp3#
所有的C和C++标准都告诉你,你需要
#include
一个给定的头文件,以便引入一些功能(在一些平台上,这甚至可能是不必要的,尽管包含是一个好主意,因为你正在编写 * 可移植 * 代码)。这为编译器提供了很大的灵活性。
链接,如果有的话,将自动完成。注意,有些函数甚至可以硬编码到编译器本身。
klh5stk14#
默认情况下,库(包含printf的实现)在C程序中每次都是链接的。
通过包含头文件,你只需要在编译时(暂时)指定声明函数的实现(在头文件中)在其他地方,然后在链接阶段,这些函数实现被“添加”到你的代码中。
r7xajy2e5#
LD知道如何搜索和找到他们。你可以看到与man ld.so:
如果共享对象依存关系不包含斜线,则按以下顺序搜索:
在编译阶段,你需要知道你要链接到什么,并进行相应的编译,所以它需要读取带有定义的
.h
文件,在链接阶段,只需要.o
文件。