动静态库的制作和原理
目录
动静态库的原理
静态库
动静态库的区别
静态库
动态库
静态库的生成与使用
静态库的生成
静态库的使用
动态库的生成与使用
动态库的生成
动态库的使用
动静态库的原理
假如我有个朋友叫张三,他总是请我吃饭,有一次老师要我们交作业,张三平时并不听课,他说他让我把方法的定义.h文件和方法的使用.c文件发给他,然后他自己去用这些方法写一个usercode文件交上去,这样就可以蒙混过关了。后来我们一直这样干,但有一天,老师发现了,我被张三连累了,扣了平时分,但是他总是请我吃饭,不好意思不给他作业抄,所以我想了个办法。老师讲过:源文件和头文件变成可执行程序有四个过程
- 预处理: 完成头文件展开、去注释、宏替换、条件编译等,最终形成
xxx.i
文件。 - 编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,最终形成
xxx.s
文件。 - 汇编: 将汇编指令转换成二进制指令,最终形成
xxx.o
文件。 - 链接: 将生成的各个
xxx.o
文件进行链接,最终形成可执行程序..
我就把写好的.c文件经过汇编形成.o文件,和.h文件打包发给张三,然后他可以将,o文件链接起来,形成一个可执行程序,这样就能躲过老师的眼睛,既能还张三的人情,也能不被老师发现,两全其美,而我们刚刚用的打包.o文件的方法就是制作一个库。库里面不包含主函数,是要用到的方法的实现,所以库的本质其实就是一个“半可执行程序”。
静态库
我们在Linux下写一份很简单的代码,
#include <stdio.h>int main()
{printf("hello world!\n);return 0;
}
我们运行这份代码
但是今天我们并不讨论这份代码,而是要讨论我们为什么可以用printf函数,这是为什么呢?因为这个程序链接的时候把C标准库也链接了,在Linux中,我们可以通过 ldd 这个命令来查看这个可执行程序依赖的库。我们可以看到我们这个程序依赖的其实是 libm.so.6 ,我们再用ls来看看这个到底是什么。
这是我们上一节所学的软链接。我们再通过 file 查看 libm-2.17.so 的文件类型,
看到了shared就知道这是一个共享目标库文件,就是动态库。
- 在Linux中,以 .so 为后缀的是动态库,以 .a 为后缀的是静态库。
- 在windows中,以 .dll 为后缀的是动态库, 以 .lib 为后缀的是静态库。
我们上面所查看的 libm.so.6 就是C动态库,我们将lib去掉,将后缀和版本号去掉就是这个库的名字。而在g++或者gcc中,都是动态链接的,如果想要静态链接,只需后面加个-static就可以了。这个静态生成的文件比原来那个要大得多,而且静态生成的可执行程序并不依赖别的库文件,我们也可以查看一下。
动静态库的区别
静态库
- 程序在编译链接的时候把库的代码链接到可执⾏⽂件中,程序运⾏的时候将不再 需要静态库。
就像我们上面执行的一样,可执行程序依赖的如果是静态库的话,以后就不需要别的库了,这就是静态库的特征。但因为平时都是将静态库直接全部复制在可执行程序中,所以特别大,占用大量空间,这也是静态库的缺点。
动态库
- 程序在运⾏的时候才去链接动态库的代码,多个程序共享使⽤库的代码。
优点就是节省磁盘空间,且多个可执行程序所共享,但是缺点就是必须依赖动态库。
静态库的生成与使用
静态库的生成
我们使用最为简单的代码来创建一个静态库吧。用最简单的数的加减,定义了两个源文件和两个头文件。内容很简单就不过多阐述了。
第一步:编译源文件,让其生成对应的目标文件(.o文件)
第二步:使用ar将其打包为静态库
ar是gnu的归档工具,rc表示(replace 和 create)。
ar -rc libcal.a add.o sub.o
既然我们打包了文件,那是不是也可以查看库里面的文件有啥,可以,用-tv选项。
ar -tv libcal.a
第三步:将生成的库和头文件组织起来
当我们将自己的库给张三用的时候,其实要给他两个文件夹,第一个是.h为后缀的头文件,第二个就是刚刚生成的库,有点麻烦,我们将他组织起来。 这样我们也算是组织起来了。
使用Makefile
其实还挺麻烦的,使用Makefile的话能帮我们省掉很多事情,我们把需要写的命令,写到里面去,执行的时候就不用我们手敲了。我们使用make就能形成静态库,使用make output就能将头文件和库组织起来。
静态库的使用
我们使用静态库呢,就需要一个main函数:
#include <stdio.h>
#include <add.h>int main()
{int x = 20;int y = 10;int z = my_add(x, y);printf("%d + %d = %d\n", x, y, z);return 0;
}
而gcc编译main.c的话,就要用到三个选项:
- -I:指定头文件搜索路径。
-L
:指定库文件搜索路径。-l
:指明需要链接库文件路径下的哪一个库。
gcc main.c -I./mathlib/include -L./mathlib/lib -lcal
至此,我们就完成了静态库的生成与使用。
动态库的生成与使用
动态库的生成
基本步骤其实和静态库差不多,但有些些差别,我们还是像静态库一样。
第一步:将它编译成目标文件,但是这里和静态库的区别就体现出来了,此时用源文件生成目标文件时需要携带
-fPIC
选项:
-fPIC
(position independent code):产生位置无关码。
第二步:使用-shared选项将所有目标文件打包为动态库
第三步:将头文件和生成的动态库组织起来
使用Makefile
就像静态库那样写一个Makefile,就能一步到位了。
我们就能轻松得到库,然后将库和头文件组织起来
动态库的使用
我们还是用之前用的mian.c来演示一下。
#include <stdio.h>
#include <add.h>int main()
{int x = 20;int y = 10;int z = my_add(x, y);printf("%d + %d = %d\n", x, y, z);return 0;
}
继续使用-I.-L.-l的命令,来生成可执行文件, 但是,与静态库不同的是,这里生成的可执行文件并不能执行。
我们先看看可执行程序所依赖的库,
所依赖的动态库并没有被找到,我们怎么解决呢?
.这里有一种解决办法:拷贝.so文件到系统共享库路径下
既然系统找不到我们的库文件,那么我们直接将库文件拷贝到系统共享的库路径下,这样一来系统就能够找到对应的库文件了。
sudo cp mlib/lib/libcal.so /lib64
至此,我们动态库的生成与使用也就完成了!