Static and dynamic libraries¶
不过我感觉官网这份讲的有点太冗长了,而且缺乏实例,因此我个人提炼总结一下,写一份更加客制化的笔记。
简单类比:
- 静态库 就像每个办公室都配一台打印机
- 动态库 就像共享一个打印室
在我自己日常的开发过程中,用到的基本都是静态库,但是实际生活中(CLI包管理器)一般都是动态库
Static Library¶
文件组成¶
静态库在编译时会被直接链接到你的程序中,成为可执行文件的一部分。
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 |
|
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 |
|
编译成静态库后(Windows上是.lib
,Linux上是.a
),使用方式:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 |
|
工程过程¶
项目结构:
Bash | |
---|---|
1 2 3 4 5 6 7 |
|
1) 创建项目专有文件夹
Bash | |
---|---|
1 2 3 |
|
在这个/lib
下面创建上述提到的三个文件:
mathlib.h
mathlib.cpp
main.cpp
2) 编译静态库
首先,将 mathlib.cpp
编译为目标文件:
Bash | |
---|---|
1 |
|
然后,使用 ar
命令创建静态库(在macOS上扩展名也是.a):
Bash | |
---|---|
1 |
|
这里的参数说明:
- r:替换库中已有的目标文件
- c:创建库(如果库不存在)
- s:创建目标文件索引
3) 编译主程序
使用静态库编译 main.cpp:
Bash | |
---|---|
1 |
|
参数说明:
-L./lib
:指定库文件搜索路径-lmathlib
:链接 libmathlib.a(注意去掉lib前缀和.a后缀)-I.
:指定头文件搜索路径(当前目录)-o main
:指定输出文件名
4) 运行可执行程序
Bash | |
---|---|
1 |
|
what is ar command
ar 命令是一个用于创建、修改和提取归档文件的工具,主要用于以下用途:
常用命令选项
- r:替换或添加文件到归档文件中
- t:显示归档文件中的内容列表
- p:显示归档文件中成员的内容
- d:从归档文件中删除成员
- c:创建新的归档文件
- s:创建归档文件的索引,用于加快访问速度
最常见的用途是在 软件开发中创建静态库:
- 将编译后的目标文件(.o文件)组合成静态库
- 创建程序链接时需要的库文件
- 生成可供链接器快速定位符号的索引
在复盘的过程中,我们可能会好奇,为什么要把 lib/mathlib.o
变成 lib/libmathlib.a
?
.o
文件(目标文件)
- 是单个源文件编译后的中间产物
- 包含机器码,但还不能直接执行
- 不能直接被其他程序使用
.a
文件(静态库文件)
- 是多个
.o
文件的集合 - 可以被其他程序链接使用
- 是最终的库文件形式
这就像是把散落的零件(.o
文件)组装成一个完整的工具包(.a
文件),其他程序就可以直接使用这个工具包了。
一个很简单的检验方式
ar -t lib/libmathlib.a
使用这条指令可以查看库的详细信息
Bash | |
---|---|
1 2 3 |
|
小结(静态库实现与使用):
- 将源文件放在项目根目录,新建
lib
文件夹 - 用与库函数相关的
.h
and.cpp
文件,编译生成.o
文件并放在/lib
下 - 用
ar
命令将.o
文件打包成.a
文件 (库函数包文件) - 编译
main.cpp
,使用上述提供的.a
库函数包文件
Dynamic Library¶
使用dynamic library的优点是可复用性很强:在全局视角下,每个功能都有自己相应的lib,供全局范围内其他所有文件引用使用
只需要修改math.h
,剩余内容/布局和在上面完全一致
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
|
1) 编译动态库
首先,将 mathlib.cpp
编译为目标文件,需要使用 -fPIC
选项(Position Independent Code):
Bash | |
---|---|
1 |
|
创建动态库(在macOS上是.dylib
, linux上是.dll
):
C++ | |
---|---|
1 |
|
2) 编译主程序
Bash | |
---|---|
1 |
|
3) 运行程序
PS: 在运行之前,需要设置动态库搜索路径
Bash | |
---|---|
1 2 |
|
在复盘的时候,我们可能会考虑,每次都要人为隐式地告诉链接路径,是不是很麻烦?
因此我们在这里介绍 方式2(显式加载)在macOS上需要使用 dlopen()
:
C++ | |
---|---|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
|
在这种条件下,我们只需要修改动态编译主函数main.cpp
的步骤:
Bash | |
---|---|
1 |
|
其余保持不变即可