谁能解释一下编译是如何工作的?

我似乎无法弄清楚编译是如何工作的..

更具体地说,这是一个例子..我正在尝试在 MSVC++ 6 中编写一些代码来加载 Lua 状态..

我已经:

  • 设置库的附加目录并将文件包含到正确的目录中
  • 使用 extern "C" (因为 Lua 仅是 C 或者所以我听说)
  • 包含正确的头文件

但我仍然在 MSVC++6 中遇到一些关于未解析的外部符号的错误(对于我使用的 Lua 函数)。

尽管我很想知道如何解决这个问题并继续前进,但我认为如果我了解所涉及的底层流程对我来说会更好,所以有人可以为此写一个很好的解释吗?我想知道的是这个过程..它可能看起来像这样:

步骤1:

  • 输入:源代码
  • 过程:解析(也许在这里添加更多细节)
  • 输出:无论这里输出什么..

第2步:

  • 输入:无论步骤 1 输出什么,再加上可能还需要其他任何内容(库?DLL?。所以?.lib?)
  • 过程:无论输入做什么
  • 输出:无论输出什么

等等..

谢谢..

也许这将解释什么是符号,什么是“链接”,什么是“对象”代码或其他什么。

谢谢..抱歉我是个菜鸟..

附:这不必是特定于语言的..但请随意用您最舒服的语言表达它。:)

编辑:所以无论如何,我能够解决错误,事实证明我必须手动将 .lib 文件添加到项目中;仅在 IDE 设置或项目设置中指定库目录(.lib 所在的位置)是行不通的。

然而,下面的答案在一定程度上帮助我更好地理解了这个过程。非常感谢!..如果有人仍然想写一份详尽的指南,请这样做..:)

编辑:只是为了提供额外的参考,我发现一位作者(Mike Diehl)写的两篇文章很好地解释了这一点。:)检查编译过程:第1部分 检查编译过程:第2部分

有帮助吗?

解决方案

对于 C 和相关语言,从源代码到可执行文件通常是一个两阶段过程,尽管 IDE 可能将其呈现为单个过程。

1/ 您编写源代码并通过编译器运行它。此阶段的编译器需要您的源代码和您要链接的其他内容的头文件(见下文)。

编译包括将源文件转换为目标文件。目标文件有你编译的代码和足够的信息来知道他们还需要什么其他东西,但是 不是 在哪里可以找到其他东西(例如,LUA 库)。

2/ 下一阶段的链接是将所有目标文件与库组合起来以创建可执行文件。我不会在这里介绍动态链接,因为这会使解释变得复杂,而且没有什么好处。

您不仅需要指定链接器可以找到其他代码的目录,还需要指定包含该代码的实际库。事实上,您收到未解决的外部信息表明您还没有这样做。

作为示例,请考虑以下简化的 C 代码(xx.c)和命令。

#include <bob.h>
int x = bob_fn(7);

cc -c -o xx.obj xx.c

这编译了 xx.c 文件至 xx.obj. 。这 bob.h 包含原型 bob_fn() 这样编译就会成功。这 -c 指示编译器生成目标文件而不是可执行文件,并且 -o xx.obj 设置输出文件名。

但实际情况 代码 为了 bob_fn() 不在头文件中,而是在 /bob/libs/libbob.so, ,因此要链接,您需要类似以下内容:

cc -o xx.exe xx.obj -L/bob/libs;/usr/lib -lbob

这创造了 xx.exexx.obj, ,使用以下形式的库(在给定路径中搜索) libbob.so (lib和.so通常由链接器添加)。在这个例子中, -L 设置库的搜索路径。这 -l 指定一个库,以便在必要时查找包含在可执行文件中的库。链接器通常采用“bob”并在指定的搜索路径中找到第一个相关的库文件 -L.

库文件实际上是目标文件的集合(类似于 zip 文件如何包含多个其他文件,但不一定是压缩的) - 当找到未定义外部的第一个相关出现时,将从库复制目标文件并添加就像你的可执行文件一样 xx.obj 文件。这通常会持续到不再有未解决的外部问题为止。“相关”库是“bob”文本的修改,它可能会寻找 libbob.a, libbob.dll, libbob.so, bob.a, bob.dll, bob.so 等等。相关性由链接器本身决定,并且应该记录下来。

它的工作原理取决于链接器,但基本上就是这样。

1/ 所有目标文件都包含需要解析的未解析外部文件的列表。链接器将所有这些对象放在一起并修复它们之间的链接(解析尽可能多的外部对象)。

2/ 那么,对于每个外部 仍然 如果未解决,链接器会梳理库文件,寻找可以满足链接的目标文件。如果找到它,就会将其拉入 - 这可能会导致进一步未解决的外部问题,因为拉入的对象可能有自己的需要满足的外部列表。

3/ 重复步骤 2,直到不再有未解析的外部文件或无法从库列表中解析它们(这是您的开发位置,因为您没有包含 LUA 库文件)。

我之前提到的复杂性是动态链接。这是您链接例程存根(某种标记)而不是实际例程的地方,稍后在加载时(当您运行可执行文件时)解析该例程。Windows 通用控件等内容都位于这些 DLL 中,因此可以更改它们,而无需将对象重新链接到新的可执行文件中。

其他提示

第 1 步 - 编译器:

  • 输入:源代码文件[s]
  • 过程:解析源代码并翻译成机器代码
  • 输出:目标文件,其中包含:
    • 该对象中定义的符号名称以及该对象文件“导出”的符号名称
    • 与此目标文件中定义的每个符号关联的机器代码
    • 未在此目标文件中定义的符号名称,但该目标文件中的软件所依赖的符号名称以及随后必须链接到的符号名称,即该目标文件“导入”的名称

第 2 步 - 链接:

  • 输入:
    • 步骤 1 中的目标文件
    • 其他对象的库(例如来自操作系统和其他软件)
  • 过程:
    • 对于您想要链接的每个对象
    • 获取该对象导入的符号列表
    • 在其他库中查找这些符号
    • 将相应的库链接到您的目标文件
  • 输出:一个可执行文件,其中包括所有对象的机器代码,以及导入(链接)到对象的库中的对象。

两个主要步骤是编译和链接。

编译采用单个编译单元(这些只是源文件,及其包含的所有标头),并创建目标文件。现在,在这些目标文件中,在特定位置(地址)定义了许多函数(以及其他内容,例如静态数据)。在下一步链接中,还需要有关这些函数的一些额外信息:他们的名字。所以这些也被存储起来。单个目标文件可以引用实际上位于其他目标文件中的函数(因为它想在运行代码时调用它们),但由于我们在这里处理单个目标文件,因此只能使用符号引用(它们的“名称”)这些其他函数存储在目标文件中。

接下来是链接(这里我们只限于静态链接)。链接是将第一步创建的目标文件(直接创建或将它们一起放入 .lib 文件后)组合在一起并创建可执行文件的位置。在链接步骤中,通过在正确的对象中查找名称、查找函数的地址并将地址放在右侧,解析从一个目标文件或 lib 到另一个目标文件或 lib 的所有符号引用(如果可以的话)地方。

现在,解释一下您需要的有关“外部“C””的内容:

C没有函数重载。函数总是可以通过其名称来识别。因此,当您将代码编译为 C 代码时,只有函数的真实名称存储在目标文件中。

然而,C++ 有一种叫做“函数/方法重载”的东西。这意味着函数的名称不再足以识别它。因此,C++ 编译器为包含函数原型的函数创建“名称”(因为名称加上原型将唯一标识一个函数)。这称为“名称修改”。

当您想要使用从 C++ 项目编译为“C”代码的库(例如,预编译的 Lua 二进制文件)时,需要“extern“C””规范。

对于您的具体问题:如果仍然不起作用,这些提示可能会有所帮助:* Lua 二进制文件是否使用相同版本的 VC++ 编译?* 您可以简单地自己编译 Lua,无论是在您的 VC 解决方案中,还是作为 C++ 代码作为单独的项目?* 您确定所有“外部“C””内容都正确吗?

您必须进入项目设置并在“链接器”选项卡上的某个位置添加一个包含 LUA 库 *.lib 文件的目录。设置称为“包括库”或其他东西,抱歉我无法查找它。

您得到“无法解析的外部符号”的原因是因为 C++ 中的编译分两个阶段进行。首先,代码被编译,每个 .cpp 文件都有自己的 .obj 文件,然后“链接器”启动并将所有 .obj 文件连接到 .exe 文件中。.lib 文件只是一堆合并在一起的 .obj 文件,以使库的分发更加简单。因此,通过添加所有“#include”和 extern 声明,您告诉编译器在某个地方可以找到具有这些签名的代码,但链接器找不到该代码,因为它不知道那些具有实际代码的 .lib 文件在哪里被放置。

确保您已阅读该库的 REDME,通常他们对将其包含在代码中必须执行的操作有相当详细的解释。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top