“程序库”就是包含了一些同音函数的数据和二进制可执行机器码的文件。这些文件是目标文件的一种,其不能单独执行。但是如果将其与其他的可执行代码结合起来就可以执行。这些目标文件通常可以完成同一类功能,他们可以作为其他执行程序的一部分来执行。由于库的存在,使得用户编写的程序模块化更强,从而可以加强程序的再编译,提高代码的复用性。
从链接方式上,程序库可分为静态库和动态库(共享库)两种:
- 静态库:是在执行程序运行前就已经加入到执行码中,成为执行程序的一部分来执行。
- 动态库:是在执行程序启动是加载到执行程序中,可以被多个执行程序共享使用。
一、静态库
1.静态库的概念
静态库是一些目标代码的集合。linux环境下的静态库目标文件一般以.a作为扩展名。Linux环境下使用ar命令创建一个静态库。静态库的优点在于使用简单,编译快速,静态库在应用程序生成时,已经编译成为可重定位的目标文件,因此可不必再编译。
由于静态库在编译时已经复制到可执行代码段中,所以不需要像使用动态库那样在程序加载时再次链接。
2.创建静态库
创建一个四则运算的静态库:
(1)四则运算的代码为:
/* 计算加法的函数 */
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;
}
(2)在shell中编译源文件,生成一个可重定位的目标文件:
gcc -c static_lib.c
生成的目标文件为:
static_lib.o
(3)Linux下使用工具ar创建一个静态库,或者将目标文件加入到一个已经存在的静态库中,使用方法如下:
ar rcs 静态库名 目标文件1 目标文件2...目标文件n
该命令将目标文件1~n加入到指定的静态库中。如果该静态库不存在,则创建静态库文件。其中ar工具的参数rcs起到了重要的作用。这三个参数分别表示:
- 参数r:把列表中的目标文件加入到静态库中
- 参数c:若指定的静态库不存在,则创建该库文件
- 参数s:更新静态库文件的索引,是指包含新加入的目标文件的内容
现在使用刚生成的.o文件创建一个静态库:
ar rcs libstatic_lib.a static_lib.o
这样就生成了一个新的静态库:
libstatic_lib.a
3.使用静态库
静态库创建成功后,需要链接到应用程序中使用。应用程序可以引用该库中的函数或者全局变量。为了是应用程序可以正确引用该库中的全局符号,需要制作一个包含该静态库中全局符号声明的头文件,这个头文件可以包含在应用程序的头文件中,这样就可以使用静态库中的全局符号了。
#include <stdio.h>
#include "static_lib.h"
int main(void)
{
int a, b;
printf("please input a and b\n");
scanf("%d %d", &a, &b);
printf("the add : %d\n", add(a, b));
printf("the sub : %d\n", sub(a, b));
printf("the mul : %d\n", mul(a, b));
printf("the div : %d\n", div(a, b));
return 0;
}
使用-l选项来指定静态库,-L参数来指定库文件的搜索路径。-l和-L之后都栉jie风沐雨带参数而不跟空格,所以用一下命令编译:
gcc -L/home/daowm/WorkSpace/C/linux/ch7 static_main.c -lstatic_lib -o static_main
或者使用 "." 表示当前目录:
gcc -L. static_main.c -lstatic_lib -o static_main
注意:-l是链接器选项,一定要放在被编译的源文件之后,即上面的static_main.c之后,若放在前面则会链接失败。
gcc也支持使用-static选项对静态库链接:
gcc static_main.c -static ./libstatic_lib.a -o static_main
以上三种方法都可以。
运行程序如下:
二、动态库
1.动态库的概念
动态库又称为共享库或者动态链接库,现代程序中大量地使用动态链接库,如windows下的dll文件和linux下的so文件,动态库是在程序启动时被装载的。
当一个引用程序装载了一个动态库后,其他应用程序仍可以装载同一个动态库,这个被多个进程同时使用的动态库在内存中只有一个副本。动态库的优点很多:
- 是代码
- 便于程序发布
- 易于模块更新,更新库并不影响程序使用旧的版本
- 在执行特定程序时,可以覆盖整个库或者更新库中的特定函数
2.创建动态库
这里使用上面的代码来演示
(1)四则运算代码
//static_lib.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;
}
(2)Linux下使用gcc创建一个动态库。由于动态库可以被多个进程共享加载,所以需要生成位置无关的目标文件。这时需要使用gcc编译器的-fPIC选项,该选项用于生成位置无关的代码。除了需要使用-fPIC选项外,还需要使用-shared选项,该选项将位置无关的代码制作为动态库。
gcc -shared -fPIC -o shared_lib.so static_lib.c
这时一个新的动态库就创建成功了。之后用户可以在自己的应用程序中引用该库中的函数,在程序加载的时候链接该库就可以了。
3.使用动态库
动态库创建成功后,应用程序可以引用该库中的函数或者全局变量。同样在.h文件包含该动态库中的全局符号:
//shared_lib.h
extern int add(int a, int b); //加法函数的声明
extern int sub(int a, int b); //减法函数的声明
extern int mul(int a, int b); //乘法函数的声明
extern int div(int a, int b); //除法函数的声明
编写程序:
//shared_main.c
#include <stdio.h>
#include "shared_lib.h"
int main(void)
{
int a, b;
printf("please input a and b\n");
scanf("%d %d", &a, &b);
printf("the add : %d\n", add(a, b));
printf("the sub : %d\n", sub(a, b));
printf("the mul : %d\n", mul(a, b));
printf("the div : %d\n", div(a, b));
return 0;
}
使用动态库编译生成可执行程序:
gcc shared_main.c ./shared_lib.so -o shared_main
运行结果同上。
同时,另外一个程序也可以使用该动态库,因为动态库是共享的。