最近因为阅读源码的工作,需要自己编译一些 .so 文件或者 .a 文件,查了一些资料,写了一些示例,记录一下。
静态库和动态库
一般程序编译的过程可以分为编译和链接两个阶段。链接阶段,需要把所有的obj文件(.o)链接起来,生成可执行程序,这个过程可以链接其他外部的库文件。 有时候也有省略写法。
gcc -c test.c #源文件编译,生成 .o 文件
gcc -o test test.o #将Obj文件链接,可以多个文件
#省略写法
gcc test.c #默认生成 a.out
gcc -o test test.c #两个阶段省略成一个
库文件里的具体实现时对用户透明的,只是提供功能函数,而用户不能知道库文件爱你的源码实现。如果你不想开源你的具体实现,你就可以只提供库文件供别人使用。库文件分为静态库和动态库文件:
静态库:一般情况下也就是 .a 文件。静态库就是指在链接的过程中,将库文件所有数据都整合到目标代码,这样生成的可执行文件执行时就不再需要外部库支持,随便哪里运行。这样就导致了静态库编译生成的可执行库文件较大,而且当库文件需要更新改变时,可执行文件也必须重新编译生成。
动态库:一般情况下就是 .so 文件。与静态库不同,动态库文件在链接时不会将所有的代码实现整合到目标代码中,而是在可执行程序执行到相应位置时才会去库文件中搜索相应的方法。所以动态库链接生成的函数就比较小,而且库文件更新时,只需要重新生成库文件就可以了,不需要重新编译可执行程序。这就给库文件升级更新带来了极大的便利。
库文件的编译生成
静态库的编译生成
静态库的编译生成需要用到 ar 指令,链接过程参数 -L 表示库文件路径,-l 表示库文件名称。库文件要以 lib{name}.a 命名格式,name指库文件的名字。
写一个测试小程序,三个文件,funTest.c, funTest.h 和 testMain.c ,testMain.c调用 一个 库函数输出:
// funTest.h 头文件进行函数接口定义
#include <stdio.h>
void printa();
//--------------------------------------------
// funTest.c 源文件中实现
#include "funTest.h"
void printa(){
printf("print a\n");
}
//-------------------------------------------
// testMain.c 主程序文件,调用库函数
#include "funTest.h"
int main()
{
printa();
return 0;
}
三个文件代码如上所示,编译过程如下所示。写完我发现,库文件的链接过程和.o文件类似,咋一看起来似乎直接使用.o文件更方便,那为什么不直接使用.o文件呢?其实一般情况下,库文件都是第三方库,而且由多个.o文件编译而成。.
# 正常情况下,就直接将object文件链接一起编译生成可执行程序就行了
gcc -c funTest.c
gcc -o testMain testMain.c funTest.o
# 库文件形式下
gcc -c funTest.c # 编译生成funa.o
ar -rsv libfunTest.a funTest.o # ar指令, 编译生成静态库文件
gcc -o testMain testMain.c -L./ -lfunTest # 链接静态库文件,生成可执行文件
动态库的编译生成
动态库即是一般是指的.so 文件,与静态库相对应,动态库在编译链接过程中并不会把所有代码都编译进去,编译成的可执行文件也必须调用动态库才能正确运行,程序执行到相应位置才会去动态库中寻找函数实现。这样编译出来的可执行程序较小,且动态库可随时升级,而不需要重新编译生成可执行文件。
动态库的编译需要用到 -fPIC 和 -shared 两个参数,本来跟静态库一样通过-L 和 -l 两个参数进行库文件路径和文件名的指定,但是到动态库的时候就没有成功,就直接指定了。
# 动态库编译生成
gcc -o libtest.so -fPIC -shared funa.c
gcc -o testMain testMain.c ./libtest.so # 直接指定动态库位置
测试程序还是同上静态库的程序,然后做个试验,将funa.c修改,再次重新生成动态库,而不再重新编译生成 testMain 可执行文件。
#include "funa.h"
void printa(){
printf("print a; after modification\n");
}
执行观察结果:结果成功改变。说明动态库的更新便捷性,不需要重新编译生成可执行文件。
库文件的依赖
生成的可执行文件可以通过查看库文件依赖看是否编译成功,命令 ldd ;如果显示找不大库文件,则是库文件链接失败。
参考资料:https://blog.csdn.net/bytxl/article/details/7609579 文章写得挺清楚,就是版面太乱了(他是转载直接复制粘贴的)。
来源:CSDN
作者:wx_14678
链接:https://blog.csdn.net/wx_14678/article/details/103597303