方法库大体上可以分为两类:静态库和动态库(共享库)。
1. windows中静态库是以 .lib 为后缀的文件,动态库是以 .dll 为后缀的文件。
2. linux中静态库是以 .a 为后缀的文件,动态库是以 .so为后缀的文件。
静态链接:
- 静态库 在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。因此对应的链接方式称为静态链接。
- 静态库可以简单看成是一组目标文件(.o .obj文件)的集合, 将若干个.o文件转换为静态库的过程,称之为打包. Linux下是使用ar工具, Windows下是使用lib.exe。
- Linux下静态链接库的后缀是.lib;Windows下静态链接库的后缀是.a。
动态链接:
- 动态库在程序编译时并不会被连接到目标代码中,而是在程序运行时才被载入。不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例,规避了空间浪费问题。动态库在程序运行是才被载入,也解决了静态库对程序的更新、部署和发布页会带来麻烦。用户只需要更新动态库即可,增量更新。
- 在Windows系统下的执行文件格式是PE(Portable Executable)格式,动态库需要一个DllMain函数做出初始化的入口,通常在导出函数的声明时需要有_declspec(dllexport)关键字。 跟exe有个main或者WinMain入口函数一样,DLL也有一个入口函数,就是DllMain。根据编写规范,Windows必须查找并执行DLL里的DllMain函数作为加载DLL的依据,它使得DLL得以保留在内存里。这个函数并不属于导出函数,而是DLL的内部函数。这意味着不能直接在应用工程中引用DllMain函数,DllMain是自动被调用的。
对于动态链接库,DllMain是一个可选的入口函数。一个动态链接库不一定要有DllMain函数,比如仅仅包含资源信息的DLL是没有DllMain函数的。
- Linux下gcc编译的执行文件默认是ELF格式,不需要初始化入口,亦不需要函数做特别的声明,编写比较方便。
无需打包工具,直接使用编译器即可创建动态库。 - Linux下动态链接库的后缀是.so;Windows下动态链接库的后缀是.dll
一、动态链接库创建和使用
1.创建hello.so动态库
#include <stdio.h>void hello(){ printf("hello world\n");}编译:gcc -fPIC -shared hello.c -o libhello.so
2.hello.h头文件
void hello();
3.链接动态库
#include <stdio.h>#include "hello.h" int main(){ printf("call hello()"); hello();}编译:gcc main.c -L. -lhello -o main
这里-L的选项是指定编译器在搜索动态库时搜索的路径,告诉编译器hello库的位置。”.”意思是当前路径.
3.编译成够后执行./main,会提示:
In function `main':
main.c:(.text+0x1d): undefined reference to `hello'
collect2: ld returned 1 exit status
这是因为在链接hello动态库时,编译器没有找到。
解决方法:
sudo cp libhello.so /usr/lib/
这样,再次执行就成功输入:
call hello()
二、静态库 创建和使用
文件有:main.c、hello.c、hello.h
1.编译静态库hello.o:
gcc hello.c -o hello.o #这里没有使用-shared
2.把目标文档归档
ar -r libhello.a hello.o #这里的ar相当于tar的作用,将多个目标打包。
程序ar配合参数-r创建一个新库libhello.a,并将命令行中列出的文件打包入其中。这种方法,如果libhello.a已经存在,将会覆盖现在文件,否则将新创建。
3.链接静态库
gcc main.c -lhello -L. -static -o main
这里的-static选项是告诉编译器,hello是静态库。
或者:
gcc main.c libhello.a -L. -o main
这样就可以不用加-static
4.执行./main
输出:call hello()
区别:
可执行文件大小不一样
从前面也可以观察到,静态链接的可执行文件要比动态链接的可执行文件要大得多,因为它将需要用到的代码从二进制文件中“拷贝”了一份,而动态库仅仅是复制了一些重定位和符号表信息。
占用磁盘大小不一样
如果有多个可执行文件,那么静态库中的同一个函数的代码就会被复制多份,而动态库只有一份,因此使用静态库占用的磁盘空间相对比动态库要大。
扩展性与兼容性不一样
如果静态库中某个函数的实现变了,那么可执行文件必须重新编译,而对于动态链接生成的可执行文件,只需要更新动态库本身即可,不需要重新编译可执行文件。正因如此,使用动态库的程序方便升级和部署。
依赖不一样
静态链接的可执行文件不需要依赖其他的内容即可运行,而动态链接的可执行文件必须依赖动态库的存在。所以如果你在安装一些软件的时候,提示某个动态库不存在的时候也就不奇怪了。
即便如此,系统中一般存在一些大量公用的库,所以使用动态库并不会有什么问题。
复杂性不一样
相对来讲,动态库的处理要比静态库要复杂,例如,如何在运行时确定地址?多个进程如何共享一个动态库?当然,作为调用者我们不需要关注。另外动态库版本的管理也是一项技术活。这也不在本文的讨论范围。
加载速度不一样
由于静态库在链接时就和可执行文件在一块了,而动态库在加载或者运行时才链接,因此,对于同样的程序,静态链接的要比动态链接加载更快。所以选择静态库还是动态库是空间和时间的考量。但是通常来说,牺牲这点性能来换取程序在空间上的节省和部署的灵活性时值得的。再加上局部性原理,牺牲的性能并不多。