C / C ++编译器如何在头文件中找到原型的定义?

当我在头文件中声明一个函数,并将该函数的定义放在其他文件中时,编译器/链接器如何找到定义? 它是否系统地搜索其路径中的每个文件,还是有更优雅的解决方案? 这一天一直困扰着我,我一直无法找到解释。

编译器不会这样做,链接器会这样做。

虽然编译器一次只能处理一个源文件,但是当调用链接器时,它会传递编译器生成的所有目标文件的名称,以及用户希望链接的任何库。因此,链接器完全了解可能包含该定义的文件集,并且只需要查看这些目标文件的符号表。 除此之外,它不需要进行任何搜索。

例如,假设您有foo.h和foo.c定义和实现函数foo() ,以及bar.h和bar.c定义和实现bar() 。 说bar调用foo以便bar.c包含foo.h. 这个编译有三个步骤:

 gcc -c foo.c gcc -c bar.c gcc foo.o bar.o -o program 

第一行编译foo.c,生成foo.o. 第二个编译bar.c,生成bar.o. 此时,在目标文件bar.o中, foo是外部符号。 第三行调用链接器,它将foo.o和bar.o链接在一起,形成一个名为“program”的可执行文件。 当链接器处理bar.o时,它会看到未解析的外部符号foo ,因此它在链接的所有其他目标文件的符号表中查找(在本例中只是foo.o)并在foo.o中找到foo ,并且完成链接。

使用库时,这有点复杂,它们在命令行中出现的顺序可能会取决于您的链接器,但它通常是相同的原则。

编译.cpp文件时,编译器会在.obj文件中输出两个表:它希望在外部定义的符号列表,以及在该特定模块中定义的符号列表。

链接器获取编译器输出的所有.obj文件,然后(顾名思义) 它们全部链接在一起。 因此,对于每个模块,它会查看标记为“外部定义”的符号列表,并查看为这些符号指定的所有其他模块。

所以它只会“搜索”你告诉它搜索的模块。

如果它在任何其他模块中找不到符号,那么当您收到“未定义的引用”错误时。

假设你有一个带有#include foo.h的foo.cpp,也许还有其他的包含。 标题当然可以有自己的#include-s。

预处理器将以foo.cpp开头,解析#includes并读取标头内容。 结果将是头文件中的文本和foo.cpp“flattened”。 然后编译器将处理该文本。 如果未在标题中的某处声明变量/ function / etc,则编译器将报告错误。

基本点是编译器必须查看.cpp和头文件的所有声明。