动态库与静态库
一.认识动静态库
- 静态库:程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库,在Linux中静态库是以(.a)为后缀;
- 动态库:程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。在Linux中静态库是以(.so)为后缀;
- 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码;
- 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking);
- 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚 拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间;
可以通过 [ ldd 可执行程序文件名 ]来查看可执行程序所依赖的库
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dfHacBYb-1680492542106)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/312213011248797.png)]
如何辨别它采用的是哪一种库呢?
- 我们可以通过后缀来区分(在Linux中)
在Linux中,以 .so 结尾的后缀,是动态库;以 .a 结尾的是静态库
在Windows中,以 .dll 结尾的后缀,是动态库;以 .lib 结尾的是静态库
注:
库文件的名字:libxxx.so 和 libxxx.a
库的真实名字:去掉lib前缀,去掉 .a 、.so后缀,剩下的就是库的名称。
2.我们还可以通过file命令查看
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HaXyTfgC-1680492542107)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/426853811236664.png)]
二.编译连接过程(略)
三.库的制作和使用
库是一个二进制文件,想要使用库(给别人使用自己的制作的库或者使用别人的库)一定是由三个部分组成:库文件、头文件、文档说明;一般这个库文件就是函数的定义,头文件就是函数声明,我们只需要将这些打包好,别人使用我们头文件所给的接口就行。
编写如下四个文件:其中源文件包含add.c和sub.c,头文件包含add.h和sub.h;用来制作静态库并打包
//add.h
#pragma once
#include <stdio.h>
extern int my_add(int x, int y);
//add.c
#include "add.h"
int my_add(int x, int y)
{
return x + y;
}
//sub.h
#pragma once
#include <stdio.h>
extern int my_sub(int x, int y);
//add.h
//sub.c
#include "sub.h"
int my_sub(int x, int y)
{
return x - y;
}
3.1 静态库的制作
3.1.1 生成二进制(.o)文件
首先使用到的的.c文件生成.o文件
gcc -c add.c
gcc -c sub.c
3.1.2 打包
我们将生成好的.o文件进行打包:
ar -rc libmymath.a add.o sub.o
ar命令是gnu的归档工具,常用于将目标文件打包为静态库,下面我们使用ar命令的-r 选项和-c选项进行打包。
- -r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件。
- -c(create):建立静态库文件。
我们可以用 ar 命令的 -t 选项和 -v 选项查看静态库当中的文件。
-t:列出静态库中的文件。
-v:显示详细的信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K0fqC6nS-1680492542109)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/133015013256830.png)]
3.1.2 发布静态库
静态库要发布出去供别人使用,只要库文件(所有的.o文件)是不够的,我们需要将其和头文件一起发布出去,别人只要看到头文件,就大致了解如何使用了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FlvSVXrw-1680492542109)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/501735613249499.png)]
3.2 静态库的使用
#include "add.h"
#include "sub.h"
int main()
{
int x = 30;
int y = 20;
int ret1 = my_add(x, y);
int ret2 = my_sub(x, y);
printf("ret1 = %d\n",ret1);
printf("ret2 = %d\n",ret2);
return 0;
}
3.2.1 方法一
我们在使用静态库进行编译链接时,需要指定头文件的所在路径,库文件的所在路径以及所要掉用的库名称
- -I:指定头文件所在路径。
- -L:指定库文件所在路径。
- -l:指明需要链接库文件路径下的哪一个库
gcc main.c -I ./lib -L ./lib -l math
3.2.2 方法二
对比我们之前在编译某个.c文件时,为什么有加上这些选项呢?。这是因为之前的库都是在系统的默认路径下,所以我们可以将我们做好的静态库拷贝到系统的默认路径下,也是可以达到不需要加这些选项的效果;但是严重不推荐。
3.3 动态库的制作
3.3.1 生成二进制(.o)文件
gcc -fPIC -c add.c -o add.o
gcc -fPIC -c sub.c -o sub.o
-fPIC:作用是告知编译器 生成位置无关代码(编译产生的代码没有绝对位置,只有相对位置);从而可以在任意地方调用生成的动态库。
3.3.2 打包
gcc -shared -o libmymath.so add.o sub.o
-shared:linux在gcc编译时加上 -shared 参数时,目的是使源码编译成动态库 .so 文件;
3.3.3 发布动态库
将库文件和所有的头文件组织起来,放到lib目录下,这样就可以发布动态库了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YzhNnW0s-1680492542110)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/65233614230371.png)]
3.4 动态库的使用
动态库的使用大致和静态库类似,但略有区别。我们先使用静态库的方法来实现动态库的链接
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Kr2scdJ0-1680492542111)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/107304114236664.png)]
能够成功编译,但是运行却报错了,为什么呢?
我们通过ldd命令列出动态库依赖关系,发现是not found。虽然已经告诉了编译器库文件和头文件的路径所在位置,但是当编译器编译好后,就与编译器无关了;当我们执行(运行)可执行程序a.out时,是由加载器来完成的。所以我们需要在运行时,告诉系统库文件在哪里;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Y2nAOVvx-1680492542111)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/519314114256830.png)]
3.4.1 方法一
拷贝到系统的默认路径下,一般指/usr/lib 这里不做演示,严重不推荐;
3.4.2 方法二
export LD_LIBRARY_PATH=/home/gxx/文档/gxx/C_Test/lib
LD_LIBRARY_PATH环境变量用于在程序加载运行期间查找动态链接库时指定除了系统默认路径之外的其他路径.注意,LD_LIBRARY_PATH中指定的路径会在系统默认路径之前进行查找;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J8hoJDvh-1680492542112)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/391224414249499.png)]
添加好后,我们再次查看,发现路径已经指定好了
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wFvoh949-1680492542113)(https://gitee.com/guixiaoxiang/picture/raw/master/linux/动态库与静态库.md/552754514246054.png)]
四、动态库与静态库特点总结
静态库的特点:
- 静态库在可执行程序链接时就加入到可执行代码中,在物理上成为可执行程序的一部分;程序运行时将不再需要该静态库。
- 相对于动态库链接生成的程序,静态函相当于编译器将代码补充完整了,因此执行程序会大一些,但是运行起来相对快些;
- 静态库是牺牲了空间效率,换取了时间效率;
动态库的特点:
- 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在;
- 动态库只有在程序执行时, 那些需要的函数代码才被拷贝到内存中。这样就使可执行文件比较小, 节省磁盘空间;
- 由于运行时要去链接库会花费一定的时间,执行速度相对会慢一些;
动态库是牺牲了时间效率,换取了空间效率;