Joe*_*oel 1 c# c++ java linker compilation
我无法理解编译器和链接器的工作方式以及它们创建的文件.更具体地说,.cpp,.h,.lib,.dll,.o,.exe如何一起工作?我最感兴趣的是C++,但也对Java和C#感到疑惑.任何书籍/链接将不胜感激!
小智 6
关于这个主题的书很少见.这里有一些想法:
除非您实际使用表驱动方法编写编译器,否则不要打扰Dragon Book.这是一个非常难读的abd并没有涵盖最简单的解析方法 - 递归下降 - 任何细节.警告:我还没看过最新版本.
如果您真的想编写编译器,请查看"在Pascal编译器上使用Brinch Hansen ",这是一个简单的阅读,并为小型pascal编译器提供完整的源代码.不要让pascal的东西让你失望 - 它所教授的课程适用于所有编译语言.
在链接方面,资源非常少.我读过的关于这个主题的最好的书是连接器和装载机.
我不认为你真的需要这方面的任何书。据我了解你的问题,你只是想知道每个类型的文件是对,他们如何与编译过程。如果您想详细了解所有内容,或者您正在编写自己的 C++ 编译器,则显然需要阅读书籍。
但这是高级版本:
首先,让我们忽略链接器。并非每种语言都使用专用链接器,事实上,即使是 C 和 C++ 语言标准也没有提到链接。链接器是一个实现细节,通常用于使所有部分组合在一起,但从技术上讲,它根本不需要存在。
此外,这是非常特定于 C/C++ 的。每种语言的编译过程都不同,尤其是 C/C++ 使用了大多数现代语言都避免的混乱、过时和低效的机制。
首先,您编写一些代码。此代码保存在多个文件(通常带有扩展名 .c、.cc 或 .cpp)和多个标题(.h、.hh 或 .hpp)中。不过,这些扩展不是必需的。它们只是一个常见的约定,但从技术上讲,您可以将文件命名为任何名称。
为了举例,让我们假设我们有以下文件:
foo.h:
void foo();
Run Code Online (Sandbox Code Playgroud)
foo.cpp:
#include "foo.h"
#include "bar.h"
void foo() {
bar();
}
Run Code Online (Sandbox Code Playgroud)
酒吧.h:
void bar();
Run Code Online (Sandbox Code Playgroud)
bar.cpp:
#include "bar.h"
void bar() {
}
Run Code Online (Sandbox Code Playgroud)
编译器获取一个.cpp 文件,并对其进行处理。假设我们首先编译 foo.cpp。它做的第一件事是预处理:展开所有宏,通过将包含文件的内容复制/粘贴到它来自 #include 的位置来处理 #include 指令。完成后,您就有了一个翻译单元或编译单元,它看起来像这样:
void foo(); //#include "foo.h"
void bar(); //#include "bar.h"
void foo() {
bar();
}
Run Code Online (Sandbox Code Playgroud)
基本上,在我们的简单示例中发生的所有事情都是标题被复制/粘贴。
现在编译器尽可能多地将其编译为机器代码。当然,鉴于它只能看到这个代码文件,它会遇到一个函数调用它看不到定义的函数。
bar()在我们的例子中它应该如何实现调用?它不能,因为它不能看到什么bar 呢。它所能看到的(因为它包含的bar.h是该函数bar 存在,并且它不接受任何参数并返回void。所以编译器基本上会生成一个“稍后填充”标签,本质上是说“跳转到这个函数的地址,如一旦我们知道那是什么地址”。
现在我们已经编译了foo.cpp.
此过程的输出是一个目标文件,通常具有扩展名 .o 或 .obj。
现在也调用了编译器bar.cpp,并且发生了几乎相同的事情。包含头文件,然后将代码编译为机器代码,尽管这一次,我们不应该遇到任何缺少定义的问题。
所以我们现在剩下foo.o并bar.o包含两个编译单元中的每一个的编译代码。
现在我们处于一个有趣的无人区,在那里 C++ 语言标准告诉我们程序应该做什么,但没有更多关于如何到达那里的内容,但程序实际上还没有这样做。我们不会有一个程序呢。所以为了解决这个问题,我们调用了链接器。
我们将所有目标文件都提供给它,它会读取它们并基本上填补空白。在阅读时foo.o,它会注意到有一个bar()对地址bar()未知的调用。但是链接器可以访问bar.o``as well, so it is able to look up the definition ofbar() , and determine its address, which it can paste into the call site inside thefoo()` 函数。它基本上将这些独立的目标文件链接在一起。当它解决了所有这些问题后,它会将所有代码一起放入一个二进制文件(在 Windows 上带有 .exe 扩展名),这就是您的程序。实际代码由编译器生成,然后链接器跳入并将一个文件中的定义与其他文件中对其的引用链接在一起。