引出
我们首先写出下面的一段很简单的c语言代码
1 #include <stdio.h> 2 3 int main() 4 { 5 printf("hello world\n"); 6 return 0; 7 }
这段代码我们在一开始学习c语言的时候就会写了 它的运行结果我们当然也很清楚 会向显示器打印 hello world
再回到上面的那段代码中 我们有没有想过这样一个问题
为什么我们调用 printf函数 就能够向屏幕打印信息呢?
通过前面基础IO和操作系统部分的学习
我们知道c语言其实是用户层的函数
它的下面有系统调用接口 系统调用接口经过封装之后变成为了我们的printf函数
而系统调用接口接受这个指令之后操作系统便指挥显示器的驱动向屏幕打印函数内的信息
也就是说我们在语言层面所直接使用的函数其实全部是别人封装好的库(lib)
而我们上面的那段代码之所以可以运行就是因为gcc编译器在生成可执行文件的时候将c语言标准库连接了进来
初识动静态库
我们怎么查看自己使用了哪些库呢?
在linux操作系统下 我们可以使用 ldd + 可执行文件名
来查看一个文件运行所依赖的库文件
这里面的 /lib64/lic.so.6
其实就是我们可执行程序所依赖的库文件
我们使用ls
指令查看该库文件 发现它是一个文件的软连接
紧接着我们使用ll
指令查看到该软连接的源文件
知道了该软连接的源文件之后我们可以使用file
指令来查看它具体的文件类型
我们可以看到 它是一个共享的目标文件库 (shared object)被用作动态连接 (dynamically linked)
怎么辨认linux下的动静态库?
- 在linux系统中动态库一般以
.so
为后缀 静态库一般以.a
为后缀
顺便提一嘴
- 在windows系统中动态库一般以
.dll
为后缀 静态库一般以.lib
为后缀
库的名字是什么呢?
我们看到的库的源文件名字是 libc-2.17.so
我们去掉前面它的标识符lib 去掉后面的后缀.so 之后剩下的就是它的名字
即库的名字为 c-2.17
在默认情况下 我们编译程序都是使用动态连接的
如果我们想要进行静态链接则需要在后面加上一个-static选项
进行静态链接之后我们可以明显发现可执行文件的大小比动态链接的可执行文件大小大得多
这是因为使用静态链接编译可执行文件的话编译器会将整个库打包到可执行文件当中
我们可以使用 ldd + 静态链接后的文件名
来证明这一点
此外我们还可以使用file
指令来查看它们是什么链接方式
动静态库的基本原理
动静态库的本质是可执行程序的“半成品”
我们都知道 语言从源文件变为可执行文件要分为下面四步
- 预处理 (去除注释 头文件展开 宏替换 条件编译)变成
.i
文件 - 编译 (将c/c++ 代码翻译成汇编代码)变成
.s
文件 - 汇编 (将汇编代码转化为二进制语言)变成
.o
文件 - 链接 (将汇编过程产生的二进制文件进行连接) 变成可执行文件
比如说 如果我们想要将五个源文件变为一个可执行文件的话我们就应该这么做
首先将这五个文件全部汇编变成.o
目标文件
之后使用gcc编译器将这五个目标文件链接
而如果我们在别的项目中也需要用到这些二进制文件的话只需要复制一份过去之后用gcc编译器进行连接就可以
如果所有的项目几乎都要频繁的用到这几个目标文件呢?
那么我们实际上就可以对于这几个文件进行打包 放到环境配置中
每次要使用的时候自动到这个环境配置里面找
这样子打包后的文件就叫做库文件
所以说 各种动静态库的本质实际上就是一堆的目标文件(.o
文件)
它们当中不包含main函数而是包含了大量的方法供使用者调用
这就是动静态库的原理 所以我们可以说动静态库的本质是可执行程序的“半成品”
动静态库的各自特征
我们将动静态库和可执行文件的概念映射到现实生活中
你在宿舍中写作业 动态库就像是网吧中的电脑
当你遇到不会的题目的时候 你就会去网吧查资料
而静态库就像是你在宿舍中买的电脑
当你遇到不会的题目的时候 你就会使用自己的电脑去查资料
静态库
静态库链接就是程序在编译连接的将静态库中的代码下载到可执行文件当中
所以说我们使用静态库生成的可执行文件一般体积比较大
自然可执行程序运行的时候也不需要依赖外部库文件了
- 优点
使用静态库链接之后 文件不需要依赖外部库文件也可以执行
- 缺点
使用静态库链接的库文件体积较大 占用空间多 且多个静态链接的可执行文件中可能会有大量的重复代码
动态库
动态库链接是程序在运行的时候才去链接相应的动态库代码 多个程序共享使用库的代码
在可执行文件开始运行前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存当中 这个过程被称为动态链接
为了节省空间 操作系统利用虚拟内存机制 将中间的一片共享区指向了物理内存中同一块动态库区域
- 优点
节省磁盘空间 避免重复代码
- 缺点
依赖库文件运行 如果缺少库文件则无法运行