假设我有以下程序(hello.c
):
#include <stdio.h>
#include <math.h>
#define NAME "ashoka"
int main(int argc, char *argv[])
{
printf("Hello, world! My name is %s\n", NAME);
}
所以,正如我所理解的那样 编译过程 这个程序是:
预处理:将复制粘贴 stdio.h中 和 文件math.h 函数声明和替换 NAME
同 "ashoka"
。
clang -E hello.c
编译: 将转向 C 代码到 部件 码
clang -S hello.c
文件制作: hello.s
组装: 转变 部件 代码到 目的 码
clang -c hello.s
文件制作: hello.o
链接:结合 目的 将文件放入我们将要执行的文件中。
clang hello.o -lm
或者(假设我也想链接hello2.o)
clang hello.o hello2.o
所以,问题来了:
该过程描述的是正确的吗?
在链接阶段,我们链接在一起 .o
(对象代码)文件。我知道 math.h
居住在 /usr/include
目录。哪里 math.o
?链接器如何找到它?
是什么 .a
(静态库)和 .so
Linux中的(动态库)?它们是如何相关的 .o
文件和链接阶段?
假设我想分享一个我与世界建立的图书馆。我有一个 mylib.c
文件,我已经声明并实现了我的功能。我将如何分享这一点,以便人们通过这样做将其包含在他们的项目中 #include <mylib.h>
要么 #include "mylib.h"
?
- 是的,虽然进行汇编是一个额外的步骤(您可以只将C源编译为对象)。在内部,编译器将有更多的阶段:将代码解析为AST,生成中间代码(例如,LLVM bitcode)
clang
),优化等
math.h
只定义标准数学库的原型 libm.a
(你链接的 -lm
)。这些函数本身存在于内部存档的目标文件中 libm.a
(见下文)。
- 静态库只是目标文件的归档。链接器将检查使用的符号,并将提取和链接导出这些符号的目标文件。这些库可以用
ar
(例如 ar -t
列出库中的目标文件)。动态(或共享)库不包含在输出二进制文件中。相反,您的代码所需的符号在运行时加载。
您只需使用您的。创建一个头文件 extern
ed原型:
#ifndef MYLIB_H
#define MYLIB_H
extern int mylib_something(char *foo, int baz);
#endif
并将其与您的图书馆一起发货。当然,开发人员还必须(对于您的图书馆)进行链接(dinamically)。
静态库的优点是可靠性:没有任何意外,因为您已经将代码与您确定可以使用的确切版本相关联。其他可能有用的情况是,当您使用不常见或前沿的库并且您不希望将它们安装为共享时。这是以增加二进制大小为代价的。
共享库生成较小的二进制文件(因为库不在二进制文件中),RAM占用空间较小(因为操作系统可以加载一次库并在多个进程之间共享),但它们需要更加小心以确保您正在加载正是你想要的(例如,见 DLL地狱 在Windows上)。
正如@iharob所说,他们的优势并不仅仅停留在二进制大小上。例如,如果在共享库中修复了错误,则所有程序都将从中受益(只要它不会破坏兼容性)。此外,共享库提供外部接口和实现之间的抽象。例如,假设OS为应用程序提供了一个库以与其进行交互。通过更新,操作系统界面会发生变化,库实现会跟踪这些更改。如果它被编译为静态库,则必须使用新版本重新编译所有程序。如果它是一个共享库,他们甚至都不会注意到它(只要它 外部 界面保持不变)。另一个示例是Linux库,它将系统/特定于发行版的方面包装到公共接口。
您在上面描述的过程是正确的。然而,在绝大多数情况下,C代码是按照以下步骤进行预处理和组装的:
clang -c hello.c
执行单独的预处理通常仅用于调试。除非您打算进行一些很少需要的手动装配级别优化,否则几乎不会转换为装配。
关于链接, -l
选项告诉链接器查找“lib {name} .so”形式的共享库。在你的例子中, -lm
告诉链接器链接libm.so.默认情况下它会在/ usr / lib中查找,但是你可以使用 -L
选项,为其提供搜索库的目录列表。
你用的是 -B
用于在链接静态库或动态库之间切换的标志:
clang hello.o -lm -Bstatic -lstaticlib -B dynamic -ldynamiclib
这将链接libm.so,libstaticlib.a和libdynamiclib.so
静态库直接链接到您的可执行文件,如.o文件。相反,动态库与可执行文件分开,并在运行时加载。
- 是的,这是一般的过程。
- 没有 math.o 文件,
-lm
切换链接到 libm.so (共享对象,因此:.so)声明数学函数所需的所有符号 文件math.h 是 定义。
让我们分两部分回答这个问题
静态库
是简单的目标文件集合以归档格式保存。
共享库
是 (在linux上)ELF文件的符号定义类似于在可执行文件中定义,您链接程序以便能够在运行时使用这些符号,并且有一个加载器将这些符号加载到要使用的程序中。
这在其他平台上几乎是一样的,比如 .DLL在Windows上,它们基本上是编译的程序,缺少一个 主要() 功能,所以他们不能直接执行。 Thye包含要在运行时加载的可执行代码。事实上你可以通过使用来实现 dlopen(3)
在linux上。
注意:在你发布的代码中,有些东西不会发生,因为你没有使用任何东西 文件math.h所以链接到 libm.so 是完全没有用的。编译器还尝试优化生成的代码,在您的情况下,程序相当于最简单的 你好,世界 在 C。但问题的其余部分是有效的,回答是有意义的。