静态库
1 命名规则
lib+name+.a
,例如:libtest.a
。
2 制作过程
其实静态库就是由很多生成的.o文件打包到一起得到的一个.a文件,所以制作过程主要有三大步。我们举一个简单的例子来说明,比如我现在要实现一个算法库,该库只实现简单的加减乘除4个功能。
-
准备原材料:
首先需要编写好必要的源文件以及头文件,我们把头文件放到include目录,源文件放到src目录,即将生成的库文件放到lib目录中,由于例子比较简单这里不列出源代码的内容了,重点看一下过程。
-
生成对应的.o文件:
这里我们使用-c选项。
-
将生成的.o文件打包:
使用ar命令:ar rcs + 静态库名子 + 生成的所有的.o
这样我们就制作好了一个静态库。
3 发布和使用
- 发布静态库
上面我们制作好的静态库需要发布给用户。 - 发布头文件
因为库文件都是一些编译生成的目标文件的集合,用户是没有源码的,所以在头文件中需要体现库中的接口,包括使用一些注释对函数功能参数以及返回值做一些说明。 - 使用
用户在使用的时候只需要包含提供的头文件,就可以调用相应的接口了。我们来看一下在编译的时候如何添加该库,下面有两种常用的方式:
方式一:gcc main.c lib/libcal.a -Iinclude -o app
方式二:gcc main.c -L lib -l cal -Iinclude -o app1
- 查看静态库:
使用nm命令:nm + 静态库名字
,可以看到静态库包含的.o文件以及文件内的函数名字和地址。
4 静态库&应用程序&可执行文件
如果使用了静态库,那么编译过程中会把静态库打包到可执行文件中。可是如果静态库很大功能很丰富,而我们的应用程序只调用了其中一个函数,难道要把整个库都打包进可执行程序吗?显然不是,下面我们来分析一下。
我们先列出库函数的源码:
再看一下main函数源码:
#include <stdio.h>
#include "head.h"
int main(int argc, char **argv)
{
int a = 10;
int b = 20;
short c = 50;
short d = 30;
int res1 = add(a, b);
short res2 = subShort(c, d);
printf("sum = %d, subShort = %d\n", res1, res2);
return 0;
}
我们在main()函数中只调用了静态库中的add()和subShort()函数,所以实际上打包到可执行文件中的静态库只有add.o和sub.o,也就是说我们使用了库中的某个函数,该函数对应的.o文件就会打包到可执行文件中,或者理解为打包的最小单位为.o文件。可以通过下图理解以下静态库,应用程序和可执行文件三者间的关系。
我们可以来验证一下,先使用nm命令查看一下可执行文件:nm 可执行文件
。重点看一下“T”表示的代码段,从中我们可以看出虽然我们没有调用addShort()函数,但是该函数仍然被打包进可执行程序中,所以打包的最小单位应该是.o文件而不是函数。而且可执行文件中没有看到mul.o和div.o中的函数出现,所以也不是将整个静态库打包。
上面nm命令输出的每行开头的数字表示函数的地址,我们可以通过反汇编代码来验证一下。执行命令:objdump -D app > cal.dis
,打开输出的cal.dis文件,我们截取关键部分,对比一下反汇编中函数的地址与上面代码中显示的函数地址完全一致。
0000000000400526 <main>:
400526: 55 push %rbp
400527: 48 89 e5 mov %rsp,%rbp
40052a: 48 83 ec 30 sub $0x30,%rsp
40052e: 89 7d dc mov %edi,-0x24(%rbp)
400531: 48 89 75 d0 mov %rsi,-0x30(%rbp)
400535: c7 45 f4 0a 00 00 00 movl $0xa,-0xc(%rbp)
40053c: c7 45 f8 14 00 00 00 movl $0x14,-0x8(%rbp)
400543: 66 c7 45 ee 32 00 movw $0x32,-0x12(%rbp)
400549: 66 c7 45 f0 1e 00 movw $0x1e,-0x10(%rbp)
40054f: 8b 55 f8 mov -0x8(%rbp),%edx
400552: 8b 45 f4 mov -0xc(%rbp),%eax
400555: 89 d6 mov %edx,%esi
400557: 89 c7 mov %eax,%edi
400559: e8 37 00 00 00 callq 400595 <add>
40055e: 89 45 fc mov %eax,-0x4(%rbp)
400561: 0f bf 55 f0 movswl -0x10(%rbp),%edx
400565: 0f bf 45 ee movswl -0x12(%rbp),%eax
400569: 89 d6 mov %edx,%esi
40056b: 89 c7 mov %eax,%edi
40056d: e8 79 00 00 00 callq 4005eb <subShort>
400572: 66 89 45 f2 mov %ax,-0xe(%rbp)
400576: 0f bf 55 f2 movswl -0xe(%rbp),%edx
40057a: 8b 45 fc mov -0x4(%rbp),%eax
40057d: 89 c6 mov %eax,%esi
40057f: bf a4 06 40 00 mov $0x4006a4,%edi
400584: b8 00 00 00 00 mov $0x0,%eax
400589: e8 72 fe ff ff callq 400400 <printf@plt>
40058e: b8 00 00 00 00 mov $0x0,%eax
400593: c9 leaveq
400594: c3 retq
0000000000400595 <add>:
400595: 55 push %rbp
400596: 48 89 e5 mov %rsp,%rbp
400599: 89 7d ec mov %edi,-0x14(%rbp)
40059c: 89 75 e8 mov %esi,-0x18(%rbp)
40059f: 8b 55 ec mov -0x14(%rbp),%edx
4005a2: 8b 45 e8 mov -0x18(%rbp),%eax
4005a5: 01 d0 add %edx,%eax
4005a7: 89 45 fc mov %eax,-0x4(%rbp)
4005aa: 8b 45 fc mov -0x4(%rbp),%eax
4005ad: 5d pop %rbp
4005ae: c3 retq
00000000004005af <addShort>:
4005af: 55 push %rbp
4005b0: 48 89 e5 mov %rsp,%rbp
4005b3: 89 fa mov %edi,%edx
4005b5: 89 f0 mov %esi,%eax
4005b7: 66 89 55 ec mov %dx,-0x14(%rbp)
4005bb: 66 89 45 e8 mov %ax,-0x18(%rbp)
4005bf: 0f b7 55 ec movzwl -0x14(%rbp),%edx
4005c3: 0f b7 45 e8 movzwl -0x18(%rbp),%eax
4005c7: 01 d0 add %edx,%eax
4005c9: 66 89 45 fe mov %ax,-0x2(%rbp)
4005cd: 0f b7 45 fe movzwl -0x2(%rbp),%eax
4005d1: 5d pop %rbp
4005d2: c3 retq
00000000004005d3 <sub>:
4005d3: 55 push %rbp
4005d4: 48 89 e5 mov %rsp,%rbp
4005d7: 89 7d ec mov %edi,-0x14(%rbp)
4005da: 89 75 e8 mov %esi,-0x18(%rbp)
4005dd: 8b 45 ec mov -0x14(%rbp),%eax
4005e0: 2b 45 e8 sub -0x18(%rbp),%eax
4005e3: 89 45 fc mov %eax,-0x4(%rbp)
4005e6: 8b 45 fc mov -0x4(%rbp),%eax
4005e9: 5d pop %rbp
4005ea: c3 retq
00000000004005eb <subShort>:
4005eb: 55 push %rbp
4005ec: 48 89 e5 mov %rsp,%rbp
4005ef: 89 fa mov %edi,%edx
4005f1: 89 f0 mov %esi,%eax
4005f3: 66 89 55 ec mov %dx,-0x14(%rbp)
4005f7: 66 89 45 e8 mov %ax,-0x18(%rbp)
4005fb: 0f b7 55 ec movzwl -0x14(%rbp),%edx
4005ff: 0f b7 45 e8 movzwl -0x18(%rbp),%eax
400603: 29 c2 sub %eax,%edx
400605: 89 d0 mov %edx,%eax
400607: 66 89 45 fe mov %ax,-0x2(%rbp)
40060b: 0f b7 45 fe movzwl -0x2(%rbp),%eax
40060f: 5d pop %rbp
400610: c3 retq
400611: 66 2e 0f 1f 84 00 00 nopw %cs:0x0(%rax,%rax,1)
400618: 00 00 00
40061b: 0f 1f 44 00 00 nopl 0x0(%rax,%rax,1)
5 静态库的优缺点
优点:
- 发布程序时,不需要提供对应的库,因为代码被加载到程序中。
- 库的加载速度快。
缺点:
- 库被打包到应用程序中,导致可执行程序体积变大。
- 库发生改变需要重新编译程序。