目录
一、动/静态库的概念回顾:
二、动态库与静态库的区别:
三、静态库的创建与使用:
1、Linux静态库命名规则:
2、静态库的创建和使用:
四、动态库的创建与使用:
1、Linux动态库命名规则:
2、动态库的创建和使用:
一、动/静态库的概念回顾:
从上一章节(基础I/O)中我们讲到,库其实就是已经写好的、成熟的、可以直接使用的代码,库里面封装了数据和函数,可以直接提供给用户进行调用。
而所谓的静态、动态是指在链接阶段如何处理库,链接成可执行文件。
编译过程:
二、动态库与静态库的区别:
静态库:
静态库在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中(通俗点就是静态库在编译的链接阶段与目标文件一起打包生成可执行文件,成为可执行文件的一部分)。所以当多个应用程序同时引用一个静态库函数时就会在内存中调用函数的多个副本,大大增加可执行文件的体积,其优点是节省编译时间。
动态库:
动态库在程序编译时并不会被链接到目标代码中,而是在程序运行是才被加载和链接。所以被调函数在内存中只有一个副本、可以实现进程之间资源的共享,且动态库可以在程序运行期间释放动态库所占用的内存。
动态链接所调用的函数代码并不会拷贝到可执行文件中,也就是说可执行文件与动态库是分开的,只会在可执行文件中加入所调用函数的描述信息,当程序装载进内存中运行时,在OS的管理下会在程序与对应的动态库之间建立链接关系,当要执行所调用动态库的函数时,会根据链接产生重定位信息,然后去执行执行动态库中对应的函数。
三、静态库的创建与使用:
1、Linux静态库命名规则:
Linux的静态库的命名规范为:"lib[xxx].a"
lib为前缀,中间的xxx为这个静态库的名字,.a为拓展名,例如一个名为test的静态库为:libtest.a
2、静态库的创建和使用:
前面说到:静态库是在链接阶段将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。我们可以试想一下静态库会与汇编生成的目标文件一起链接为可执行文件,那么静态库必定跟.o文件格式相似。其实一个静态库可以简单看成是一组目标文件(.o/.obj文件)的集合,即很多目标文件经过压缩打包后形成的一个文件。
下面用一段简单的代码编译成静态库:
函数声明: add.h
int add(int x, int y);函数定义: add.cpp
int add(int x, int y)
{return x + y;
}
Linux下使用ar工具、Windows下vs使用lib.exe,将目标文件压缩到一起,并且对其进行编号和索引,以便于查找和检索。
一般创建静态库的流程如下:
通过上面的流程图可以看到,我们要先把代码文件编译成目标文件.o
g++ -c add.cpp
然后通过ar工具将目标文件打包成.a静态库文件(将add.o打包成libadd.a)
ar -crv libadd.a add.o
静态库的使用:
这里我们写一段使用库中函数的代码用来测试:
Linux下使用静态库一般为:
g++ Test.cpp -L ./ -l add
选项:
-L:表示要连接的库所在目录
-l:指定链接时所需要的库,去掉前缀lib和后缀,如上就是libadd.a去掉前后缀
此时我们删掉静态库可以发现:程序还是一样能正常运行,因为静态库已经成为这个可执行文件的一部分了,不需要依赖外界了(如下图)
四、动态库的创建与使用:
1、Linux动态库命名规则:
Linux的动态库的命名规范为:"lib[xxx].so"
lib为前缀,中间的xxx为此动态库的名字,so为拓展名,例如一个名为test的动态库为:libtest.so
2、动态库的创建和使用:
前面说到:动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入。所以动态库只需要在内存中只存在一份拷贝即可,就能有效避免静态库浪费空间的问题,且对程序进行更新也只需要更新动态库,增量更新即可。
针对于实际库文件,每个共享库都有个特殊的名字"soname"。在程序启动后,程序会通过这个名字来告诉动态加载器该载入哪个共享库。
在文件系统中,soname仅是一个链接到实际动态库的链接。对于动态库而言,每个库实际上都有另一个名字给编译器来用。它是一个指向实际库镜像文件的链接文件 (lib+soname+.so)
创建动态库文件:这里我直接复用上面的代码
函数声明: add.h
int add(int x, int y);函数定义: add.cpp
int add(int x, int y)
{return x + y;
}
生成动态库的命令为:g++ -fPIC -shared -o libadd.so add.cpp
选项:
-fPIC:创建与地址无关的编译程序(pic:position independent code),实现在多个应用程序间进行共享。
-shared:指定生成动态链接库。(生成.o文件)
-o:重命名,将打包出的库文件命名成libadd.so
其实将整条命令拆分一下就是:
g++ -fPIC -c add.cpp (生成.o文件)
g++ -shared -o libadd.so add.o (生成.so库)
注意下面这张图的a.out并不是动态库生成的,是上面静态库生成的。
动态库的使用:(Test.cpp代码还是和上面一样)
g++ Test.cpp -L ./ -l add (引用动态库编译成可执行文件跟静态库的方式一样)
但是此时我们运行生成的可执行文件会发现这样子会报错!!!
这是因为编译器只会去库目录和环境变量中找动态库,Linux环境下的库文件一般都放在 /lib 或者 /usr/lib 目录下,所以我们得要让程序能找到动态库。
解决方法:
1、将生成的动态库拷贝到 /usr/lib 或者 /lib 目录下
(这种方法虽然简单直接,但是不推荐用这种方法,因为这会污染系统的库源)
2、修改环境变量 LD_LIBRARY_PATH
(环境变量 LD_LIBRARY_PATH是动态库的搜索路径,一般情况下为空,可执行文件运行时,会去这个环境变量中搜索动态库路径,但是注意:新建终端或者重启就会失效)
3、建立软链接:
(直接在lib或lib64目录下建立一个指向这个动态库的一个软链接,推荐这种用法)
(对于32位程序一般会链接到 /usr/lib 中的库。而64位程序则会链接到 /usr/lib64 中的库。但也有例外情况,例如:如果64位系统上的某个64位程序需要向后兼容32位库,它可能仍然会链接到 /usr/lib 中的库。)
补充:可以通过ldd 命令可以查看当前执行文件所链接的动态库
如果将对应需要的动态库删除的话那这里(红色框处)就会显示找不到库,自然程序也就无法运行了
如下:如果删除动态库,程序就会无法运行:
动态库更新:
更新时只需更新动态库即可,就不需要连同使用库的应用程序一起进行编译了,如下:更改了+的逻辑变成-,只需对动态库进行重新编译就可以实现程序更新了: