编译过程
谁能解释一下编译是如何工作的?
我似乎无法弄清楚编译是如何工作的..
更具体地说,这是一个例子..我正在尝试在 MSVC++ 6 中编写一些代码来加载 Lua 状态..
我已经:
- 设置附加目录对于库并将文件包含到正确的目录中,
- 使用 extern "C" (因为 Lua 仅是 C 语言,所以我听说)
包含正确的头文件
但我仍然在 MSVC++6 中遇到一些关于未解析的外部符号的错误(对于我使用的 Lua 函数)。
尽管我很想知道如何解决这个问题并继续前进,但我认为如果我了解所涉及的底层流程对我来说会更好,所以有人可以为此写一个很好的解释吗? 我想知道的是这个过程..它可能看起来像这样:
第 1 步:
- 输入:源代码
- 过程:解析(也许在此处添加更多详细信息)
- 输出:此处输出的任何内容..
第 2 步:
- 输入:无论第 1 步输出什么,加上可能还需要其他任何内容(库?DLL?.so?.lib?)
- 进程:对输入执行的任何
- 操作 输出:输出的任何内容
等等..
谢谢..
也许这会解释什么是符号,到底什么是“链接”,什么是“对象”代码或其他什么..
谢谢..抱歉我是个菜鸟..PS
这不一定是特定于语言的..但请随意表达使用您最熟悉的语言.. :)
编辑:所以无论如何,我能够解决错误,事实证明我必须手动将 .lib 文件添加到项目; 简单地在 IDE 设置或项目设置中指定库目录(.lib 所在的位置)是行不通的。
但是,下面的答案在一定程度上帮助我更好地理解了该过程。 非常感谢!...如果有人仍然想写一份完整的指南,请这样做..:)
编辑:仅供参考,我找到了一位作者(Mike Diehl)的两篇文章来解释这一点很好.. :) 检查编译过程:第 1 部分 检查编译过程:第 2 部分
Can anyone explain how compilation works?
I can't seem to figure out how compilation works..
To be more specific, here's an example.. I'm trying to write some code in MSVC++ 6 to load a Lua state..
I've already:
- set the additional directories for the library and include files to the right directories
- used extern "C" (because Lua is C only or so I hear)
include'd the right header files
But i'm still getting some errors in MSVC++6 about unresolved external symbols (for the Lua functions that I used).
As much as I'd like to know how to solve this problem and move on, I think it would be much better for me if I came to understand the underlying processes involved, so could anyone perhaps write a nice explanation for this? What I'm looking to know is the process.. It could look like this:
Step 1:
- Input: Source code(s)
- Process: Parsing (perhaps add more detail here)
- Output: whatever is output here..
Step 2:
- Input: Whatever was output from step 1, plus maybe whatever else is needed (libraries? DLLs? .so? .lib? )
- Process: whatever is done with the input
- Output: whatever is output
and so on..
Thanks..
Maybe this will explain what symbols are, what exactly "linking" is, what "object" code or whatever is..
Thanks.. Sorry for being such a noob..
P.S. This doesn't have to be language specific.. But feel free to express it in the language you're most comfortable in.. :)
EDIT: So anyway, I was able to get the errors resolved, it turns out that I have to manually add the .lib file to the project; simply specifying the library directory (where the .lib resides) in the IDE settings or project settings does not work..
However, the answers below have somewhat helped me understand the process better. Many thanks!.. If anyone still wants to write up a thorough guide, please do.. :)
EDIT: Just for additional reference, I found two articles by one author (Mike Diehl) to explain this quite well.. :)
Examining the Compilation Process: Part 1
Examining the Compilation Process: Part 2
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
两个主要步骤是编译和链接。
编译采用单个编译单元(这些只是源文件,及其包含的所有标头),并创建目标文件。 现在,在这些目标文件中,在特定位置(地址)定义了许多函数(以及其他内容,例如静态数据)。 在下一步链接中,还需要有关这些函数的一些额外信息:它们的名称。 所以这些也被存储起来。 单个目标文件可以引用实际上位于其他目标文件中的函数(因为它想在运行代码时调用它们),但由于我们在这里处理单个目标文件,因此只能使用符号引用(它们的“名称”)其他函数存储在目标文件中。
接下来是链接(这里我们只限于静态链接)。 链接是将第一步创建的目标文件(直接创建或将它们一起放入 .lib 文件后)组合在一起并创建可执行文件的位置。
在链接步骤中,通过在正确的对象中查找名称、查找函数的地址并将地址放在右侧,解析从一个目标文件或 lib 到另一个目标文件或 lib 的所有符号引用(如果可以的话)地方。
现在,解释一下您需要的有关“extern“C””的内容:
C 没有函数重载。 函数总是可以通过其名称来识别。 因此,当您将代码编译为 C 代码时,只有函数的真实名称存储在目标文件中。
然而,C++ 有一种叫做“函数/方法重载”的东西。 这意味着函数的名称不再足以识别它。 因此,C++ 编译器为包含函数原型的函数创建“名称”(因为名称加上原型将唯一标识一个函数)。 这称为“名称修改”。
当您想要使用从 C++ 项目编译为“C”代码的库(例如,预编译的 Lua 二进制文件)时,需要“extern“C””规范。
对于您的确切问题:如果仍然不起作用,这些提示可能会有所帮助:
* Lua 二进制文件是否使用相同版本的 VC++ 编译?
* 您可以简单地自己编译 Lua,无论是在您的 VC 解决方案中,还是作为 C++ 代码作为单独的项目?
* 您确定所有“外部“C””内容都正确吗?
The two main steps are compilation and linking.
Compilation takes single compilation units (those are simply source files, with all the headers they include), and create object files. Now, in those object files, there are a lot of functions (and other stuff, like static data) defined at specific locations (addresses). In the next step, linking, a bit of extra information about these functions is also needed: their names. So these are also stored. A single object file can reference functions (because it wants to call them when to code is run) that are actually in other object files, but since we are dealing with a single object file here, only symbolic references (their 'names') to those other functions are stored in the object file.
Next comes linking (let's restrict ourselves to static linking here). Linking is where the object files that were created in the first step (either directly, or after they have been thrown together into a .lib file) are taken together and an executable is created.
In the linking step, all those symbolic references from one object file or lib to another are resolved (if they can be), by looking up the names in the correct object, finding the address of the function, and putting the addresses in the right place.
Now, to explain something about the 'extern "C"' thing you need:
C does not have function overloading. A function is always recognizable by its name. Therefore, when you compile code as C code, only the real name of the function is stored in the object file.
C++, however, has something called 'function / method overloading'. This means that the name of a function is no longer enough to identify it. C++ compilers therefore create 'names' for functions that include the prototypes of the function (since the name plus the prototype will uniquely identify a function). This is known as 'name mangling'.
The 'extern "C"' specification is needed when you want to use a library that has been compiled as 'C' code (for example, the pre-compiled Lua binaries) from a C++ project.
For your exact problem: if it still does not work, these hints might help:
* have the Lua binaries been compiled with the same version of VC++?
* can you simply compile Lua yourself, either within your VC solution, or as a separate project as C++ code?
* are you sure you have all the 'extern "C"' things correct?
您必须进入项目设置并在“链接器”选项卡上的某个位置添加一个包含 LUA 库 *.lib 文件的目录。 设置称为“包括库”或其他东西,抱歉我无法查找它。
您得到“无法解析的外部符号”的原因是因为 C++ 中的编译分两个阶段进行。 首先,代码被编译,每个 .cpp 文件都有自己的 .obj 文件,然后“链接器”启动并将所有 .obj 文件连接到 .exe 文件中。 .lib 文件只是一堆合并在一起的 .obj 文件,以使库的分发更加简单。
因此,通过添加所有“#include”和 extern 声明,您告诉编译器在某个地方可以找到具有这些签名的代码,但链接器找不到该代码,因为它不知道那些具有实际代码的 .lib 文件在哪里被放置。
确保您已阅读该库的 REDME,通常他们对将其包含在代码中必须执行的操作有相当详细的解释。
You have to go into project setting and add a directory where you have that LUA library *.lib files somewhere on the "linker" tab. Setting called "including libraries" or something, sorry I can't look it up.
The reason you get "unresolved external symbols" is because compilation in C++ works in two stages. First, the code gets compiled, each .cpp file in it's own .obj file, then "linker" starts and join all that .obj files into .exe file. .lib file is just a bunch of .obj files merged together to make distribution of libraries just a little bit simplier.
So by adding all the "#include" and extern declaration you told the compiler that somewhere it would be possible to find code with those signatures but linker can't find that code because it doesn't know where those .lib files with actual code is placed.
Make sure you have read REDME of the library, usually they have rather detailed explanation of what you had to do to include it in your code.
您可能还想查看一下:编译器、汇编器、链接器和加载器:一个简短的故事 。
You might also want to check this out: COMPILER, ASSEMBLER, LINKER AND LOADER: A BRIEF STORY.
对于 C 和相关语言,从源代码到可执行文件通常是一个两阶段过程,尽管 IDE 可能将其呈现为单个过程。
1/ 您编写源代码并通过编译器运行它。 此阶段的编译器需要您的源代码和您要链接的其他内容的头文件(见下文)。
编译包括将源文件转换为目标文件。 对象文件包含您编译的代码和足够的信息来了解它们需要哪些其他内容,但不在哪里可以找到其他内容(例如,LUA 库)。
2/ 下一阶段的链接是将所有目标文件与库组合起来以创建可执行文件。 我不会在这里讨论动态链接,因为这会使解释变得复杂,而且没有什么好处。
您不仅需要指定链接器可以找到其他代码的目录,还需要指定包含该代码的实际库。 事实上,您收到未解决的外部信息表明您还没有这样做。
作为示例,请考虑以下简化的 C 代码 (
xx.c
) 和命令。这会将
xx.c
文件编译为xx.obj
。bob.h
包含bob_fn()
的原型,以便编译成功。-c
指示编译器生成目标文件而不是可执行文件,-o xx.obj
设置输出文件名。但
bob_fn()
的实际代码不在头文件中,而是在/bob/libs/libbob.so
中,因此要链接,您需要类似的东西:这会使用
libbob.soxx.obj
创建xx.exe
code> (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 中,因此它们可以更改,而无需将对象重新链接到新的可执行文件中。
From source to executable is generally a two stage process for C and associated languages, although the IDE probably presents this as a single process.
1/ You code up your source and run it through the compiler. The compiler at this stage needs your source and the header files of the other stuff that you're going to link with (see below).
Compilation consists of turning your source files into object files. Object files have your compiled code and enough information to know what other stuff they need, but not where to find that other stuff (e.g., the LUA libraries).
2/ Linking, the next stage, is combining all your object files with libraries to create an executable. I won't cover dynamic linking here since that will complicate the explanation with little benefit.
Not only do you need to specify the directories where the linker can find the other code, you need to specify the actual library containing that code. The fact that you're getting unresolved externals indicates that you haven't done this.
As an example, consider the following simplified C code (
xx.c
) and command.This compiles the
xx.c
file toxx.obj
. Thebob.h
contains the prototype forbob_fn()
so that compilation will succeed. The-c
instructs the compiler to generate an object file rather than an executable and the-o xx.obj
sets the output file name.But the actual code for
bob_fn()
is not in the header file but in/bob/libs/libbob.so
, so to link, you need something like:This creates
xx.exe
fromxx.obj
, using libraries (searched for in the given paths) of the formlibbob.so
(the lib and .so are added by the linker usually). In this example,-L
sets the search path for libraries. The-l
specifies a library to find for inclusion in the executable if necessary. The linker usually takes the "bob" and finds the first relevant library file in the search path specified by-L
.A library file is really a collection of object files (sort of how a zip file contains multiple other files, but not necessarily compressed) - when the first relevant occurrence of an undefined external is found, the object file is copied from the library and added to the executable just like your
xx.obj
file. This generally continues until there are no more unresolved externals. The 'relevant' library is a modification of the "bob" text, it may look forlibbob.a
,libbob.dll
,libbob.so
,bob.a
,bob.dll
,bob.so
and so on. The relevance is decided by the linker itself and should be documented.How it works depends on the linker but this is basically it.
1/ All of your object files contain a list of unresolved externals that they need to have resolved. The linker puts together all these objects and fixes up the links between them (resolves as many externals as possible).
2/ Then, for every external still unresolved, the linker combs the library files looking for an object file that can satisfy the link. If it finds it, it pulls it in - this may result in further unresolved externals as the object pulled in may have its own list of externals that need to be satisfied.
3/ Repeat step 2 until there are no more unresolved externals or no possibility of resolving them from the library list (this is where your development was at, since you hadn't included the LUA library file).
The complication I mentioned earlier is dynamic linking. That's where you link with a stub of a routine (sort of a marker) rather than the actual routine, which is later resolved at load time (when you run the executable). Things such as the Windows common controls are in these DLLs so that they can change without having to relink the objects into a new executable.
第 1 步 - 编译器:
步骤 2 - 链接:
Step 1 - Compiler:
Step 2 - Linking: