在一个软件项目中,为了完成特定功能,除了自定义函数,还可以使用别人已经封装好的函数库,如 C 函数库。库函数的使用避免了重复“造笼子”的重复工作,提高了代码复用率,大大减轻了软件开发的工作量。
库分为静态库和动态库。
- 静态库。如果在项目中引用了库函数,则在编译时,链接器会将引用的函数代码或变量,链接到可执行文件里,和可执行程序组装在一起,这种库被称为静态库,即在编译阶段链接的库。
- 动态库。在编译阶段不参与链接,不会和可执行文件组装在一起,而是在程序运行时才被加载到内存参与链接,因此又叫动态链接库。
静态库的本质其实就是可重定位目标文件的归档文件,使用 ar
命令就可以将多个目标文件打包为一个静态库。
jiaming@jiaming-pc:~/Documents/CSDN_Project$ cat test.c
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
jiaming@jiaming-pc:~/Documents/CSDN_Project$ cat main.c
#include <stdio.h>
int add(int, int);
int main(void)
{
int sum = 0;
sum = add(1, 2);
printf("sum=%d\n", sum);
return 0;
}
jiaming@jiaming-pc:~/Documents/CSDN_Project$ gcc -c test.c # 将源文件 test.c 生成目标文件 test.o
jiaming@jiaming-pc:~/Documents/CSDN_Project$ ar -rcs libtest.a test.o # 使用 ar 命令将多个目标文件打包成 libtest.a
jiaming@jiaming-pc:~/Documents/CSDN_Project$ gcc main.c -L. -ltest # 指定需要链接的静态库及其所在的路径,库的名字 test
jiaming@jiaming-pc:~/Documents/CSDN_Project$ ./a.out
sum=3
ar
命令:
-c
:禁止在创建库时产生的正常消息-r
:如果指定的文件已经存在则替换-s
:无论库是否更新都强制重新生成新的符号表-d
:从库中删除指定的文件-o
:对压缩文档成员进行排序-q
:向库中追加指定文件-t
:打印库中的目标文件-x
:解压库中的目标文件
编译器是以源文件为单位进行编译程序的,链接器在链接过程中逐个对目标文件进行分解组装,这样会产生一个问题:如果在一个源文件中定义了 100 个函数,而只使用了其中一个,那么链接器在链接时也会把这 100 个函数逇代码指令全部组装到可执行文件中,这会让最终生成的可执行文件体积大大增加。
jiaming@jiaming-pc:~/Documents/CSDN_Project$ readelf -s a.out
Symbol table '.symtab' contains 70 entries:
Num: Value Size Type Bind Vis Ndx Name
···
50: 000000000000118b 24 FUNC GLOBAL DEFAULT 16 add
···
53: 00000000000011d0 23 FUNC GLOBAL DEFAULT 16 div
···
64: 0000000000001149 66 FUNC GLOBAL DEFAULT 16 main
65: 00000000000011b9 23 FUNC GLOBAL DEFAULT 16 mul
···
68: 00000000000011a3 22 FUNC GLOBAL DEFAULT 16 sub
···
解决这个问题的办法是每一个函数均由一个独立的 C 程序实现,然后将多个文件(.o
)打包即可(libtest.a
)。C 标准库就是这么实现的,printf
函数由 printf.c
实现。如果只调用了一个 printf 函数,则链接器只是将 pritnf() 函数的目标文件链接到可执行文件中即可。
静态链接还会产生另外一个问题,如 C 标准库里 printf() 函数,可能多个程序都调用了它,链接器在链接时就要将 printf 的指令添加到多个可执行文件中。在一个多任务环境中,当多个进程并发运行时,内存中有大量重复的 printf 指令代码,很浪费内存资源。
解决办法是动态链接机制!