目录
一、什么是库
库是写好的现有的,成熟的,可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。库有两种:静态库(.a、.lib) 和 动态库(.so、.dll)。
所谓静态、动态是指链接。
静态库的介绍
静态库实际就是一些目标文件(一般.o结尾)的集合,静态库一般以.a结尾,只用于生成可执行文件阶段。之所以成为静态库,是因为在 链接阶段 ,会将汇编生成的目标文件.o与引用到的库一起 链接打包 到可执行文件中。因此对应的链接方式称为 静态链接。
以下是静态库的部分特点总结:
- 静态库对函数库的链接是放在编译时期完成的。
- 程序在运行时与函数库再无瓜葛,移植方便。
- 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
动态库的介绍
动态库的意义:
- 因为静态库浪费空间和资源的却缺点,所以需要一种新的设计来节省资源。
- 静态库的另一个问题对程序的更新、部署和发布页会带来麻烦。因为有时只是一个小小的更新,而静态库却需要将程序全部更新,即全量更新。
动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,这是一种增量更新。
动态库的特点:
-
动态库把对一些库函数的链接载入推迟到程序运行的时期。
-
可以实现进程之间的资源共享。(因此动态库也称为共享库)
-
将一些程序升级变得简单。
-
甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)
静态库与动态库的区别
载入时刻不同
- 静态库在程序编译时会链接到目标代码中,程序运行时不再需要静态库,因此体积较大。而且每次编译都需要载入静态代码,因此内存开销大。
- 动态库在程序编译时不会被链接到目标代码中,而是在程序运行时才被载入,程序运行时需要动态库存在,因此体积较小。而且系统只需载入一次动态库,不同程序可以得到内存中相同的动态副本,因此内存开销小。
二、Linux下的一个实例
配置环境VMware16、Ubantu 18.04
例子程序源代码
- main.c
#include"sub1.h"
#include"sub2.h"
#include<stdio.h>
int main()
{
int x=2,y=9,n;
printf("%d/%d=%.2f\n",y,x,x2x(x,y));
printf("Please enter the Nth Fibonacci value:");
scanf("%d",&n);
printf("Fibonacci's %dth value is%d\n",n,x2y(n));
return 0;
}
- sub1.h
#ifndef __SUB1_H
#define __SUB1_H
float x2x(int a,int b);
#endif
sub1.c
#include "sub1.h"
float x2x(int a,int b)
{
float ans;
ans=(float)b/a;
return ans;
}
- sub2.h
#ifndef __SUB2_H
#define __SUB2_H
int x2y(int);
#endif
- sub2.c
#include "sub2.h"
int x2y(int n)
{
int a=1,b=1,i;
long c=0;
if(n<3) return 1;
else
{
for(i=0;i<=n-3;i++)
{
c=a+b;
a=b;
b=c;
}
return c;
}
}
创建文件成功
静态库文件的创建与使用
1.创建并编译静态库文件
- 将mian.c sub1.c sub2.c文件编译生成.o文件
无论静态库,还是动态库,都是由.o 文件创建的。因此,我们必须将源程序 hello.c 通过 gcc 先编译成.o文件。在系统提示符下键入以下命令得到 hello.o 文件。
此处输入命令gcc -c main.c sub1.c sub2.c得到 main.o、sub1.o、sub2.o三个目标文件
- 由.o 文件创建静态库。
静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为 .a,创建静态库用 ar 命令。
如果为比较大的项目会编写 makefile文件 (CMake等等工程管理工具) 来生成静态库,输入多个命令会比较麻烦。
此处输入命令ar -crv libx2.a sub1.o sub2.o生成静态库文件libx2.a
2.连接并生成可执行文件main
- 使用命令gcc main.o libx2.a -o main令文件libx2.a与main.o进行连接,并生成可执行文件
- 键入命令./main即可执行文件,结果如下。结果与目标结果相同。
- 删除静态库文件验证是否连接成功
程序照常运行,静态库中的公用函数已经连接到目标文件中了。
动态库文件的创建与使用
1. 编译动态库文件libx2.so :
动态库文件名命名规范和静态库文件名命名规范类似,也是在动态库名增加前缀 lib,但其 文件扩展名为 .so。
(生成动态库文件同样是需要 .o文件的,但之前生成静态库文件时已经生成了,此处省略)
- 输入命令gcc -shared -fPIC -o libx2.so sub1.o sub2.o进行编译生成动态库文件libx2.so
2. 连接并生成可执行文件main
使用命令gcc main.o libx2.so -o main链接两个目标文件生成可执行文件 main
- 使用命令./main尝试执行该可执行文件main
报错:此时程序无法执行成功,显示有错误No such file or directory
原因: 运行程序时, 需要在 /usr/lib 、/lib 等目录中查找需要的动态库文件,如未找到文件就会出现上述的错误。
解决方法: 将 libx2.so 复制到目录 /usr/lib 中,再次执行./main运行(若提示权限不够则以使用 sudo mv libx2.so /usr/lib 该命令)
再次使用命令./main运行可执行程序,程序成功运行。
静态库文件与动态库文件的对比
-
静态库:静态库文件2768kb,可执行文件8560kb
-
动态库:动态库文件7496kb,可执行文件8504kb
-
比较总结:从上述结果可以看出,静态库所生成的中间文件libx2.a比动态库生成的中间文件libx2.so要小很多;但生成的可执行文件 main 动态库更小,但相差不大。
从这里也可以明白,动态库虽然有调用方便的特点,但是其生成的文件是比较占用的空间的。
三、总结
本文简要介绍了静态库与动态库的原理,并在linux环境下,对动态库与静态库进行了原理上的验证、性能上的比较,验证的结果与预期结果是相同的,我本人也对“库”的原理有了更加深入的理解。