【 声明:版权所有,欢迎转载,请勿用于商业用途。 联系信箱:feixiaoxing @163.com】
关于编译,大家都比较熟悉了。无非就是把c/c++编译成动态库,或者是编译成可执行程序。还有一种情况呢,其实是编译成静态库。说起来是静态库,其实就是把很多.o文件pack在一起,不做筛选,也不做link,就仅仅是打包在一起而已。这么一看,好像也人畜无害。但有的时候,特别是link的时候,很容易出现依赖关系的时候,今天不妨找个例子看一看。
1、首先有一个add.c
这个add.c纯粹就是用来做实验的,假设它的内容是这样的,
int add()
{
return 1;
}
函数内容比较简单,我们可以通过两行命令,就可以生成对应的静态库文件libadd.a,
gcc -c add.c
ar rc libadd.a add.o
2、再准备一个calculate.c
这里的calculate.c同样比较简单,实现也不复杂。不过它和add.c有点关系,也就是calculate.c里面函数的实现依赖于add.c。虽然现在还没有进行链接,但是将来肯定要bind在一起的,
extern int add();
int calculate()
{
return add();
}
通过代码,不难发现,这里的实现也的确是依赖于add函数。上面的extern,其实也是在告诉编译器,这个函数本文件没有,直接从其他地方找吧。大体就是这个意思。和add.c一样,我们现在也把这个文件编译成静态库libcalcualte.a的形式,整个过程也是两行命令就可以搞定,
gcc -c calculate.c
ar rc libcalculate.a calculate.o
3、准备最后一个main.c
有了两个静态库,那么就可以开始准备最后一个main.c。既然我们用到了静态库,那么最好的方法,就是在main.c里面直接调用calculate.c里面的函数,再让calculate.c去调用add.c。所以我们的main.c准备写成这个样子,
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
extern int calculate();
int main(int argc, char* argv[])
{
return calculate();
}
很明显,main.c依赖于calculate函数,而calculate函数又依赖于add函数,所以一个比较直观的想法,就是编译的时候,需要把这两个静态库都添加进来。因此,我们继续进行操作。只是操作的时候,出现了戏剧性的一幕。如果我们是这么来敲命令,即
gcc main.c -o main ./libadd.a ./libcalculate.a
那么很遗憾,编译器告诉我们链接是失败的,
feixiaoxing@feixiaoxing-VirtualBox:~/Desktop/code$ gcc main.c -o main ./libadd.a ./libcalculate.a
/usr/bin/ld: ./libcalculate.a(calculate.o): in function `calculate':
calculate.c:(.text+0xe): undefined reference to `add'
collect2: error: ld returned 1 exit status
但是如果反过来呢,也就是libadd.a和libcalculate.a交换一下位置,那么link居然是成功的,
feixiaoxing@feixiaoxing-VirtualBox:~/Desktop/code$ gcc main.c -o main ./libcalculate.a ./libadd.a
feixiaoxing@feixiaoxing-VirtualBox:~/Desktop/code$
这说明什么道理呢?明明libcalculate.a是依赖于libadd.a的,但如果把libcalculate.a放在了最后面,那么它里面的函数其实也就找不到链接了。换句话说,gcc命令当中,越是基础的.a,就越应该出现在命令行的末端;而越复杂的.a,则需要出现在离可执行文件越近的位置。侧面也说明了,哪怕是静态库,编译链接的时候也要注意依赖顺序,不然链接可能过不了。