提问人:Joel 提问时间:7/30/2010 最后编辑:Tyler McHenryJoel 更新时间:7/30/2010 访问量:6906
C/C++ 编译器如何在头文件中找到原型的定义?
How does a C/C++ compiler find the definitions of prototypes in header files?
问:
当我在头文件中声明一个函数,并将该函数的定义放在其他文件中时,编译器/链接器如何找到定义?它是否系统地在其路径中的每个文件中搜索它,或者是否有更优雅的解决方案?这几天一直困扰着我,我一直无法找到解释。
答:
编译器不这样做,链接器这样做。
虽然编译器一次处理一个源文件,但当调用链接器时,它会传递编译器生成的所有目标文件的名称,以及用户希望链接的任何库的名称。因此,链接器完全了解可能包含定义的文件集,并且只需查看这些对象文件的符号表。除此之外,它不需要进行任何搜索。
例如,假设您有 foo.h 和 foo.c 定义和实现函数,并且 bar.h 和 bar.c 定义和实现。说出调用,使 bar.c 包含 foo.h。此编译有三个步骤:foo()
bar()
bar
foo
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.o 和 bar.o 链接到一个名为“program”的可执行文件中。当链接器处理 bar.o 时,它会看到未解析的外部符号,因此它会在所有其他正在链接的对象文件(在本例中只是 foo.o)的符号表中查找,并在 foo.o 中找到,并完成链接。foo
foo
foo
对于库,这有点复杂,它们在命令行上的显示顺序可能很重要,具体取决于链接器,但原则通常相同。
评论
foo.o
编译.cpp文件时,编译器会在.obj文件中输出两个表:它期望在外部定义的符号列表,以及在该特定模块中定义的符号列表。
链接器采用编译器输出的所有.obj文件,然后(顾名思义)将它们全部链接在一起。因此,对于每个模块,它都会查看标记为“外部定义”的符号列表,并查看为这些符号提供的所有其他模块。
因此,它只会“搜索”您告诉它搜索的模块。
如果它在任何其他模块中都找不到该符号,则会出现“未定义引用”错误。
评论
假设你有一个 foo.cpp #include foo.h 和其他包含。标头当然可以有自己的 #include。
预处理器将从 foo.cpp 开始,解析 #includes 并读取标头内容。结果将是头文件中的文本和 foo.cpp“扁平化”。 然后,编译器将处理该文本。如果变量/函数/等应该有 在标头中的某处声明未找到,编译器将报告错误。
基本点是编译器必须看到其所有声明作为.cpp和标头的结果。
评论