|
| 1 | +--- |
| 2 | +icon: simple/cplusplus |
| 3 | +--- |
| 4 | + |
| 5 | +# 链接库 |
| 6 | + |
| 7 | +!!! note "主要作者" |
| 8 | + |
| 9 | + [@luojh][luojh] |
| 10 | + |
| 11 | +!!! warning "本文编写中" |
| 12 | + |
| 13 | +!!! comment "适用范围" |
| 14 | + |
| 15 | + 本文介绍 Linux 上的**静态链接库** (一般为 `.a` 文件) 和**动态链接库** (一般为 `.so` 文件)。注意:这里的链接库是指包含了可执行二进制代码的库,并不是头文件 (Header file)、Python 库等。 |
| 16 | + |
| 17 | +## 总览 |
| 18 | + |
| 19 | +库 (Library),是包含**可重用代码和数据**的模块。 |
| 20 | + |
| 21 | +### 构建链接库 |
| 22 | + |
| 23 | +下图给出了一般的可执行文件 (executable)、静态链接库 (static library)、动态链接库 (dynamic library) 的构建过程。 |
| 24 | + |
| 25 | +```mermaid |
| 26 | +flowchart TD |
| 27 | +
|
| 28 | +source["源文件 (*.c, *.cpp)"] -->|编译| target["目标文件 (*.o)"] |
| 29 | +header["头文件 (*.h)"] --- source |
| 30 | +target -->|链接| executable["可执行文件 (ELF等)"] |
| 31 | +target -->|ar rcs| staticlib["静态链接库 (*.a)"] |
| 32 | +target -->|gcc -shared| dynamiclib["动态链接库 (*.a)"] |
| 33 | +``` |
| 34 | + |
| 35 | +### 使用链接库 |
| 36 | + |
| 37 | +下图给出了**使用链接库**的方式。 |
| 38 | + |
| 39 | +```mermaid |
| 40 | +flowchart TD |
| 41 | +source["源文件 (*.c, *.cpp)"] -->|编译| target["目标文件 (*.o)"] |
| 42 | +header["头文件 (*.h)"] --- source |
| 43 | +target --> linker["链接器"] |
| 44 | +staticlib["**静态链接库** (链接步骤加入)"] --> linker |
| 45 | +linker --> executable["可执行文件 (ELF等)"] |
| 46 | +dynamiclib["**动态链接库** (运行时加载)"] --> executable |
| 47 | +``` |
| 48 | + |
| 49 | +## 源代码 |
| 50 | + |
| 51 | +对于 C 程序而言,链接库的源代码就是普通函数、变量之类,并无太多特殊要求。例如,创建并进入目录 `lib`,在 `square.c` 源文件中写一个函数 `square` |
| 52 | + |
| 53 | +```c |
| 54 | +int square(int x) |
| 55 | +{ |
| 56 | + return x * x; |
| 57 | +} |
| 58 | +``` |
| 59 | +
|
| 60 | +对应的,需要在头文件 `square.h` 中加入这个函数的原型 (prototype) |
| 61 | +
|
| 62 | +```c |
| 63 | +int square(int x); |
| 64 | +``` |
| 65 | + |
| 66 | +以便其他使用链接库的程序**知道如何使用这个函数** (即使这些程序不知道函数内部的实现)。 |
| 67 | + |
| 68 | +因为链接库不是完整的、可以独立运行的程序,因此不需要入口点 (比如 `main` 函数)。 |
| 69 | + |
| 70 | +## 静态链接库 |
| 71 | + |
| 72 | +静态链接库是在**链接步骤就加入**的链接库类型。 |
| 73 | + |
| 74 | +### 构建 |
| 75 | + |
| 76 | +首先按照正常方法编译得到 `.o` 文件: |
| 77 | + |
| 78 | +```shell |
| 79 | +gcc square.c -c -o square.o |
| 80 | +``` |
| 81 | + |
| 82 | +然后使用 `ar` 程序创建静态链接库 `libsquare.a`: |
| 83 | + |
| 84 | +```shell |
| 85 | +ar rcs libsquare.a square.o |
| 86 | +``` |
| 87 | + |
| 88 | +注意这里的 `libsquare.a` 是命名惯例:一般静态链接库的文件名需要为 `lib<名称>.a`。 |
| 89 | + |
| 90 | +到这里就完成了静态链接库的创建。 |
| 91 | + |
| 92 | +### 使用 |
| 93 | + |
| 94 | +另一个 C 程序 `main.c` 中使用了 `square` 函数,那么首先需要包含 `square.h` 头文件,这样才能知道这个函数的原形,然后就可以正常调用了。 |
| 95 | + |
| 96 | +```c |
| 97 | +#include <main.c> |
| 98 | +#include "lib/square.h" |
| 99 | + |
| 100 | +int main(void) |
| 101 | +{ |
| 102 | + int a; |
| 103 | + scanf("%d", &a); |
| 104 | + |
| 105 | + printf("%d^2 = %d\n", a, square(a)); |
| 106 | + return 0; |
| 107 | +} |
| 108 | +``` |
| 109 | +
|
| 110 | +下面的命令可以将这个程序直接编译-链接到可执行文件 `main` (也可以分开成单独的编译步骤、链接步骤)。 |
| 111 | +
|
| 112 | +```shell |
| 113 | +gcc main.c -L./lib -lsquare -o main |
| 114 | +``` |
| 115 | + |
| 116 | +这里,`-L./lib` 表示要求链接器在 `./lib` 中寻找链接库,`-lsquare` 表示需要链接 `libsquare.a` 这个头文件。 |
| 117 | + |
| 118 | +编译好之后,就可以正常使用了。由于静态链接库中的代码会被直接合并到链接产生的可执行文件 `main` 中,因此运行时不需要文件 `libsquare.a`。 |
0 commit comments