我无法区分构建和编译之间的区别。它们相同吗?链接到底是如何工作的? .so 文件和 .o 文件到底包含什么以及我应该如何使用它们?这些文件我每天都会看到,但我不知道它们到底包含什么。谁能建议一些教程来清楚地讲述这些过程?
答案1
术语“构建”通常用于表示从一组源代码文件和其他资源开始,到一组可执行文件、共享库(可能还有其他资源)结束的整个过程。
这可能涉及很多步骤,例如特殊的预处理器(moc
例如 Qt 代码)、代码生成器(flex
/yacc
或bison
例如)、编译、链接以及可能的后处理步骤(例如构建tar.gz
或rpm
分发文件)。
对于 C 和 C++(以及相关语言),编译是将源文件(.c
例如 C 代码文件)转换为目标文件 ( .o
)。这些目标文件包含编译器为相应源代码生成的机器代码,但不是最终产品 - 特别是,外部函数(和数据)引用未解析。从这个意义上说,它们是“不完整的”。
目标文件有时会组合在一起形成档案(.a
文件),也称为静态库。这几乎只是将它们分组在一起的一种便捷方式。
链接需要(通常是几个)目标文件(.o
或.a
)和共享库,组合目标文件,解析(主要)目标文件本身和共享库之间的引用,并生成您可以实际使用的可执行文件或共享库(.so
)可以被其他程序或共享库使用。
共享库是可以由其他可执行文件直接使用的代码/函数的存储库。针对共享库的动态链接与直接(静态)链接对象或存档文件之间的主要区别在于,可以更新共享库而无需重建使用它们的可执行文件(尽管对此有很多限制)。
例如,如果在某个时刻在 OpenSSL 共享库中发现错误,则可以在该代码中进行修复,并且可以生成和发布更新的共享库。动态链接到该共享库的程序不需要重新构建即可修复错误。更新共享库会自动修复其所有用户。
如果他们改为与目标文件链接(或一般静态链接),他们将不得不重建(或至少重新链接)以获得修复。
一个实际的例子:假设您想用 C 语言编写一个程序 - 一个精美的命令行计算器,它具有命令行历史记录/编辑支持。您将编写计算器代码,但将使用该readline
库进行输入处理。
您可以将代码分为两部分:数学函数(将这些函数放入mathfuncs.c
)和处理输入/输出的“主”计算器代码(放入main.c
)。
你的建造包括:
Compile mathfuncs.c(
gcc -o mathfuncs.o -c mathfuncs.c
代表-c
“仅编译”)
mathfuncs.o
现在包含已编译的数学函数,但不是“可执行的” - 它只是函数代码的存储库。编译你的前端(
gcc -o main.o -c main.c
)
main.o
同样只是一堆函数,不可运行链接您的计算器可执行文件,链接为
readline
:gcc -o supercalc main.o mathfuncs.o -lreadline # ^ executable ^ dynamic link with libreadline.so # ^ ^ two .o files statically linked in
现在您有了一个可以运行的真正可执行文件 (
supercalc
),这取决于readline
库。构建一个
rpm
包含所有可执行文件和共享库(和头文件)的包。 (这些.o
文件是临时构建产品而不是最终产品,通常不会被发送。)
这样,如果在 中发现错误readline
,您无需重建(并重新发布)可执行文件即可获得修复 -libreadline.so
只需更新即可。但是,如果您发现 中的错误mathfuncs.c
,则需要重新编译并重新链接supercalc
(并发布新版本)。