1.4 编译库:静态库、动态库
一、静态库
1.1 编译静态库
静态库:是将多个.o文件打包成一个归档文件(.a)
源码下载地址:universe.zip
(1)编译源码
gcc -c -o zeus.o zeus.c
gcc -c -o sun.o sun.c
gcc -c -o moon.o moon.c
gcc -c -o earth.o earth.c
(2)将.o文件打成包静态库
ar -rcs libstar.a moon.o sun.o earth.o# ar是Linux归档工具
# -r 如果库中有同名的.o文件,则替换
# -c 如果静态库不存在,则创建。
# -s 为静态库生成或更新索引
(3)使用静态库
poseidon.c 内容如下:
#include "sun.h"
#include "moon.h"
#include "earth.h"
#include <stdio.h>
int main()
{printf("poseidon do something \r\n");sun_rotate();moon_rotate();earth_rotate();return 0;
}
# 1.编译
gcc -c poseidon.c# 2.链接静态库
gcc -o poseidon poseidon.o -static -L./ -lstar # 通过-L指定库路径gcc -o poseidon poseidon.o libstar.a # 通过库完整名称来指定
1.2 封装静态库
封装静态库:在静态库基础上,增加一些功能代码,再重新打包静态库。
封装静态库流程:
- 将新增的.c文件编译成.o文件
- 将静态库解压:
ar -x
- 将所有的.o文件重新打包成静态库
项目代码:handes.zip
(1)编译新增代码
gcc -c -o dog.o dog.c
gcc -c -o pig.o pig.c
(2)解压原静态库
# libstar.a会解压出:earth.o、moon.o、sun.o
ar -x libstar.a
(3)重新打包
ar -rcs libpower.a earth.o moon.o sun.o dig.o pig.o
注意:
- 不能直接把libstar.a和新.o文件一起打包,否则会有问题。
- 静态库不是纯ELF文件,而是多个ELF文件的合并,所以必须先解压再打包
二、动态库
2.1 编译动态库
动态库:多个程序共用,共享节省磁盘空间。
源码下载地址:universe.zip
(1)编译源码
gcc -c -o sun.o sun.c
gcc -c -o moon.o moon.c
gcc -c -o earth.o earth.c
(2)编译动态库
# 编译动态库 -fPIC 生成位置无关的代码,-shared生成动态库
gcc -fPIC -shared -o libstar.so sun.o moon.o earth.o
(3)使用动态库
# 链接动态库
gcc -c zeus.c
gcc -o zeus zeus.o libstar.so # 运行报错,找不到libstar.so,因为Linux默认不从当前目录加载动态库。
./zeus# 可以设置环境变量 再运行
export LB_LIBRARY_PATH=./:$LB_LIBRARY_PATH
./zeus
2.2 显式使用动态库
问题:当静态库和动态库存在同名符号时,会导致符号冲突。
如:
- 静态库A(依赖C标准库libc.a)和 动态库B(依赖C标准库libc.so)
- 现在程序program需要同时链接A和B库。
- A和B存在相同的符号(函数)就会导致符号冲突
解决方法:正常链接静态库,动态库在代码中显式调用。
zeus.c 的代码:
#include <stdio.h>#include<dlfcn.h>int main(int argc, char **argv){// 动态库的句柄void *handle;// 动态库中函数的地址int (*sun_rotate)();// 打开动态库handle = dlopen("/home/oem/Desktop/dev/cpp/universe/libstar.so", RTLD_LAZY);if (!handle) {fprintf(stderr, "%s\n", dlerror());return 1;}// 获取函数的地址sun_rotate = dlsym(handle, "sun_rotate");if (!sun_rotate) {fprintf(stderr, "%s\n", dlerror());return 1;}// 调用函数printf("zeus2 do something\n");(*sun_rotate)();// 关闭动态库dlclose(handle);return 0;}
编译和链接
# 会提示一个警告,但不影响运行
gcc -c zeus.c
gcc -c zeus zeus.o -ld -static
2.3 封装静态库为动态库
封装静态库成动态库:在已有的静态库基础上,增加一些功能代码,并封装成新动态库。
封装动态库流程:
- 将新增的.c文件编译成.o文件
- 将静态库解压:
ar -x
- 将所有.o文件重新链接成动态库。
项目代码:handes.zip
(1)编译新增代码
gcc -c -o dog.o dog.c
gcc -c -o pig.o pig.c
(2)解压原静态库
# libstar.a会解压出:earth.o、moon.o、sun.o
ar -x libstar.a
(3)重新打包
gcc -fPIC -shared -o libpower.so dog.o pig.o moon.o earth.o sun.o
2.4 混合使用静态库与动态库
编译器支持同时链接静态库和动态库:
-Wl,-Bstatic
指定后续库静态链接。-Wl,-Bdynamic
指定后续库动态链接。
示例项目
- 项目用到动态库libstar.so和静态库libcook.a
- 动态库存储在:/usr/local/star/lib
- 静态库存储在:/usr/lib
gcc -c theseus.c
gcc -o theseus theseus.o -Wl,-Bstatic -lcook -Wl,-Bdynamic -L/usr/local/star/lib -lstar
# 静态库不使用-L指定库路径的原因,是/usr/lib系统默认的库路径
注意:避免同一库同时静态和动态链接,防止冲突。