Linux下静态库的创建与使用:ar工具、静态库命名规范、编译与链接流程
静态库,说白了就是一堆目标文件(.o)的打包集合。你写了很多功能函数,不想每次都重新编译一遍,那就把它们揉成一个静态库。链接的时候,链接器会把库中你需要的代码直接拷贝到最终的可执行文件里。
我个人习惯把静态库看作「代码的快递包裹」——你寄出去什么,收件人就拿到什么,完全独立,不依赖外部环境。嗯,这跟动态库的思路完全不同,后面我们会细说。
静态库的命名规范
Linux下静态库的命名有固定套路,别自己瞎起名字。格式是:
lib<库名>.a
比如你写了一个数学工具库,库名叫mymath,那最终生成的静态库文件就是libmymath.a。注意三点:
- 前缀必须是
lib,这是硬性规定 - 后缀必须是
.a,代表archive(归档文件) - 中间才是你自定义的库名
我在项目中遇到过新手直接把目标文件命名为mymath.a,结果链接器死活找不到。你想想看,链接器找库的时候,默认就是去搜libxxx.a这种模式,少了lib前缀它根本不认。
ar工具:静态库的打包利器
ar是GNU Binutils套件里的一个工具,专门用来创建、修改、提取归档文件。静态库本质上就是一个ar归档文件。
常用命令格式:
ar rcs lib<库名>.a <目标文件1.o> <目标文件2.o> ...
参数含义:
| 参数 | 含义 |
|---|---|
| r | 替换或插入文件到归档中 |
| c | 创建归档,如果不存在则新建 |
| s | 建立索引,加快链接速度 |
rcs三个参数一起上,省事。尤其是s参数,它会在库文件头部生成一个符号索引表,链接器查找符号时直接查表,不用遍历整个库。没有这个索引,链接会慢很多。
查看库里的内容:
ar t libmymath.a
提取某个目标文件:
ar x libmymath.a add.o
编译与链接流程
整个流程分三步走,我画了一张图帮你理清思路:
第一步:编译源文件为目标文件
gcc -c add.c sub.c mul.c
这一步生成add.o、sub.o、mul.o。注意-c参数表示只编译不链接。
第二步:打包成静态库
ar rcs libmymath.a add.o sub.o mul.o
把三个目标文件打包成一个静态库。嗯,这里要提醒一句:库里的目标文件顺序会影响链接结果。如果函数A调用了函数B,那A的.o文件应该放在B的前面。不过加了s参数建立索引后,大多数情况下顺序问题就不那么敏感了。
第三步:链接主程序与静态库
gcc main.c -L. -lmymath -o app
参数说明:
-L.:告诉链接器在当前目录(.)下搜索库文件-lmymath:链接libmymath.a,注意这里省略了lib前缀和.a后缀-o app:指定输出文件名
-lmymath放在了main.c前面,结果链接报了一堆未定义引用。为什么?因为链接器是从左到右处理输入文件的,如果库出现在主程序之前,链接器还不知道需要哪些符号,就会跳过这个库。正确的做法是把库放在源文件或目标文件的后面。
验证静态库是否链接成功
用file命令看看生成的可执行文件:
file app
app: ELF 64-bit LSB executable, x86-64, dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, not stripped
注意看,它显示的是dynamically linked?别慌,这是说可执行文件本身依赖了C运行时动态库(libc.so),并不是说你的静态库变成了动态链接。静态库的代码已经完整拷贝到app里了。
验证方法很简单:
ldd app
linux-vdso.so.1 (0x00007ffd3a5e0000)
libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f8a1a200000)
/lib64/ld-linux-x86-64.so.2 (0x00007f8a1a400000)
看到没?只有libc.so.6,没有libmymath.so之类的东西。这说明libmymath.a里的代码已经被静态链接进去了。
libmymath.a删掉,app照样能跑。这就是静态链接最大的特点——部署简单,但文件体积大,更新库需要重新编译。
我个人习惯在项目初期用静态库,因为调试方便,不用操心库的版本兼容问题。等代码稳定了,再考虑要不要转成动态库。你想想看,如果是一个只有几个源文件的小工具,搞动态库反而增加了部署的复杂度。
最后补充一个实用命令,查看静态库里到底有哪些符号:
nm libmymath.a
它会列出每个目标文件中定义的全局符号。如果链接时提示未定义引用,先用nm查一下库里的符号名对不对。我遇到过有人把函数名拼错了,结果链接器找了半天找不到,最后发现是add写成了ad——这种低级错误,查符号表一眼就能看出来。