静态库 & 共享库
静态库 & 共享库
我们先来介绍一下库文件:
在我们编程过程中,有一些公共代码是需要重复使用的,为了方便使用,我们就将这些公共代码封装成一个库文件,用的时候直接链接这个库就可以了,非常方便。
库是分享公共程序代码的方式,一般分为静态库与共享库。
那么静态库与共享库有什么区别呢?
- 静态库:在链接时是将库文件代码拷贝一份到可执行文件中,如果库文件代码更新了,对之前生成的可执行文件没有影响,所以如果要让可执行文件功能也更新,则需要重新编译链接生成新的可执行文件。
- 共享库(动态库):在链接时不会复制库文件代码,只是将要添加的动态库的路径告诉系统,在程序运行时单独由系统动态加载到内存中,供程序使用,所以如果库文件代码更新了,那么执行先前的可执行文件,系统还是会从这个路径去取动态库,由于这时动态库已经更新了,所以不需要重新编译链接生成新的可执行文件,功能也会自动更新。
那么静态库与共享库分别在Linux系统下与Windows系统下的文件后缀是什么?
静态库: Linux -->(.a) Windows --> (.lib)
共享库: Linux --> (.so) Windows --> (.dll)
共享库相比于静态库的优点:
- 节省磁盘空间,可执行文件中并不包含库中的内容,共享库单独存储一份。
- 节省内存空间,执行程序时,多个程序用到同一个共享库时,则只需要加载一份即可。
静态库的创建:
要加载的功能代码:mymath.c
#include <stdio.h>
int myadd(int a, int b)
{
return a + b;
}
int mysub(int a, int b)
{
if(a < b)
{
return b - a;
}
return a - b;
}
①:首先,将所有的功能代码编译生成中间文件.o(mymath.o)
因为这里只需要一个中间文件mymath.o,所以只编译mymath.c
②然后,通过ar工具将所有的中间文件打包生成一个静态库文件.a(libmymath.a)
静态库的使用:
编写使用上面创建的静态库的测试代码:
Linux系统下使用静态库,只需要在编译的时候,指定静态库的搜索路径(-L选项)和静态库名(-l选项,这里注意不需要lib前缀和.a后缀)
接下来将libmymath.a直接合并到最终的可执行文件中:
- -L:表示要链接的静态库路径,这里只需要相对路径“.”即可
- -l:指定链接时需要的静态库名,编译器查找库时有隐含的命名规则,即在给出的库名前加上lib,后面加上.a或.so来确定库的真正名称。
最后我们执行一下可执行文件main,看看运行结果:
接下来,我们更新一下静态库代码:
再看一下执行可执行文件main的结果:
我们会发现,静态库的更新对我们已经生成好的可执行文件没有一点点的影响,因为最初在生成的时候已经拷贝了一份静态库的代码到可执行文件中去了,之后的操作一直使用的是当初拷贝的静态库代码,所以没有任何影响。
我们再来看一看共享库,也就是动态库。
共享库的创建:
要加载的功能代码:mymath.c
#include <stdio.h>
int myadd(int a, int b)
{
return a + b;
}
int mysub(int a, int b)
{
if(a < b)
{
return b - a;
}
return a - b;
}
我们直接一步生成动态库文件:
共享库的使用:
编写使用上面创建的共享库的测试代码:
Linux系统下使用共享库,只需要在编译的时候,指定共享库的搜索路径(-L选项)和共享库名(-l选项,这里注意不需要lib前缀和.a后缀)
接下来将libmymath.so直接合并到最终的可执行文件中:
最后我们执行一下可执行文件main,看看运行结果:
这时我们会发现系统报错了,说的是载入共享库时出错了,原因是找不到共享库,但是当前目录下是有这个libmymath.so的,那为什么系统找不到呢?
那这里我们就需要知道系统是如何去找我们需要的共享库的:
操作系统加载动态库时,默认搜索两个路径:
- /lib
- usr/lib
如果这两个路径下找不到,系统会报错,说没有找到指定的动态库
所以这个时候,我们要给操作系统说明一下,如果那两个目录找不到,就来这个地址目录找一找(同时将动态库文件给这个目录下拷贝一份):
------------------------------------------------------------------------------->/*这是一条分割线*/
当然这里只是针对当前终端有效的,重新开启一个终端,我们会发现还是找不到这个共享库。
所以我们要修改bash的配置文件,加上export LD_LIBRARY_PATH=/home/quanqiang/lib这句话,让其对所有终端都有效果:
- /etc/bashrc 针对整个系统
- /home/quanqiang/.bashrc 针对特定用户“quanqiang”
我们再看一下为什么要加上这句话:
- LIBRARY_PATH环境变量:指定程序静态链接库文件的搜索路径
- LD_LIBRARY_PATH环境变量:指定程序动态链接库文件的搜索路径
这个时候我们再执行一下可执行文件main,看看运行结果:
我们发现这个时候可以成功运行了。
接下来,我们更新一下共享库代码:
再看一下执行可执行文件main的结果:
我们发现,只需要更新一下动态库文件,不需要重新编译链接生成新的可执行文件,就可以更新其功能。这也是动态库的特点所在!
最后,我们提出一下问题:同一个路径下,有同名的静态库和动态库,如果使用“gcc -o main main.c -L. -lmymath”来生成可执行文件,那么系统默认使用哪个库文件?
我们测试一番,并通过ldd命令查看main链接的动态库是否有libmymath.so:
我们这时可以得出结论,同一个路径下,如果有同名的静态库和动态库,系统优先链接的是动态库。
注意事项:
- 静态库和动态库都是公共代码共享的,所以生成的时候不能包含其调用main.c
- 如果同一个目录下有同名的静态库文件和动态库文件,那么gcc默认链接的是动态库文件
- ldd命令可以查看一个可执行文件所链接的动态库情况
- 如果要指定要链接的是静态库,则要加上-static的gcc链接选项,强制使用静态链接库。