对于静态库的使用,网上多数文章讲的是一级嵌套,例如 main.c 引用了 myMath.a 静态库。但是如果 myMath.a 又引用了另一个静态 myTest.a 会怎么样。
gcc -o 生成可执行文件的时候,需不需要 指定 -L myTest.a?还是编译出 myMath.a 的时候 myTest.a 的二进制内容已经包含进去 myMath.a 里面了。
又或者,如果 myMath 是一个动态库,又二级嵌套引用又会怎样,带着这些疑问,咱们开始研究,代码如下:
myTest.h :
int my_test(int a, int b);
myTest.c :
#include "myTest.h"
int my_test(int a, int b) {
return a + b;
}
myMath.h :
int add(int a, int b);
myMath.c:
#include "myMath.h"
#include "myTest.h"
int add(int a, int b) {
return my_test(a,b);
}
main.c :
#include <stdio.h>
#include "myMath.h"
int main()
{
int a = 3, b = 5;
printf("a+b=%d\n", add(3, 5));
return 0;
}
代码已经写好,先执行一下命令编译出 .o 后缀的文件。
gcc -c myMath.c -o myMath.o
gcc -c myTest.c -o myTest.o
gcc -c main.c -o main.o
首先,编译嵌套的静态库是这样的,首先我们的 myMath 依赖 myTest,但是我们想编译出来的 libmyMath.a 静态库可以直接给 main 工程使用,main 不需要知道有 myTest 的存在。
要编译 libmyMath.a ,常规的做法是 gcc -c myMath.c -o myMath.o
,把 C 代码编译成 object 文件,然后用 ar rcs libmyMath.a myMath.o
把 object 文件打包成 .a 静态库文件。
ar 这个命令不太好理解,《Linux ar命令》,网上的文章讲他是一个打包备存命令。我一开始也挺纳闷,打包,备存跟静态库有啥关系。
后来,我明白,ar 确实是一个打包命令。.a
静态库实际上就是一个或多个.o文件的集合。.o 后缀的文件本身其实就是一个"静态库",请看下面的编译命令
gcc main.o myMath.o myTest.o -o main
如上,我不用 .a 的文件,也能编译出来 main 执行文件。只需要把 main.o
myMath.o
myTest.o
这3个 .o 文件合在一起编译就行了,所以你可以理解,.o 文件他本身就是一个静态库。
但是,我们的需求是,把 myTest 隐藏,只需要给一个 myMath 给客户使用即可。客户不需要知道有 myTest 的存在。上面那种编译方式把 myTest.o 暴露了,而且如果myMath 依赖很多其他的 myTest2,myTest3,要丢一堆 .o 文件给别人使用不太好。
这个时候,ar 命令的用处就出来,ar 确实就是打包的意思。可以把一堆 .o 文件 打包到一个 .a 文件里面。请看如何操作:
纠正:下面这个命令这样编译出来的 libmyMath.a 应该是不能用的,要先解压,然后 链接所有的 .o
ar -rcs libmyMath.a myMath.o myTest.o
接下来,我们使用 notepad++ 查看 libmyMath.a myMath.o myTest.o 这3个文件的内容,如下:
从上图可以看出,libmyMath.a 的内容,实际上就是myMath.o myTest.o 两个文件内容的集合,只不过 libmyMath.a 加了一些头部信息。
现在已经编译出 libmyMath.a 静态库了,下面就展示如何基于 libmyMath.a 编译出 main 可执行文件。
#第一种
gcc main.o libmyMath.a -o main
#第二种
gcc main.o -o main -static -lmyMath -L ./
第二种编译方式,-L 是我们比较常用的方式,把一个外部静态库 .a 安装到某个目录 /usr/lib/,然后用 -L 指定lib的搜索路径。
这样,就隐藏了 ,myTest 库的代码。只提供 libmyMath.a 给别人就能使用。
重点知识:
1,生成 .o (object)文件 跟 生成可执行文件,都是用 -o 来指定的。-o 代表指定 output file (输出文件),gcc 编译器应该是根据后缀名决定要输出什么样的内容。例如 .o 后缀就是输出 object 格式的内容,如果没有后缀,就是输出可执行的二进制内容。
相关阅读:
由于笔者的水平有限, 加之编写的同时还要参与开发工作,文中难免会出现一些错误或者不准确的地方,恳请读者批评指正。如果读者有任何宝贵意见,可以加我微信 Loken1。