Linux静态库与动态库详解
gcc编译器
1、gcc工作流程

2、gcc常用参数
| 参数 | 用途 |
|---|---|
| -v | 查看版本 |
| -o | 产生目标文件 |
| -I+目录 | 指定头文件目录 |
| -D | 编译时定义宏 |
| -00/-01/-03 | 没有优化/缺省值/优化级别最高 |
| -Wall | 提示更多警告信息 |
| -c | 只编译子程序 |
| -E | 生成预处理文件 |
| -g | 包含调试信息 |
什么是库文件?
库文件是事先编译好的方法的合集。比如:我们提前写好一些数据公式的实现,将其打包成库文件,以后使用只需要库文件就可以,不需要重新编写。
Linux系统中:
1.静态库的扩展名为.a;
2.动态库的扩展名为.so;
源代码到可执行程序的转换时需要经历如下图所示的过程:
1.编译是指把用高级语言编写的程序转换成相应处理器的汇编语言程序的过程。
2.汇编是从汇编语言程序生成目标系统的二进制代码(机器代码)的过程。
3.链接是指将汇编生成的多段机器代码组合成一个可执行程序。
通过编译和汇编过程,每一个源文件将生成一个目标文件。连接器的作用就是将这些目标文件组合起来,组合的过程包括了代码段、数据段等部分的合并,以及添加相应的文件头。
最后得到的可执行文件如何作用的:

ELF文件格式包括三种主要的类型:可执行文件、可重定向文件、共享库。
1.可执行文件(应用程序)
可执行文件包含了代码和数据,是可以直接运行的程序。
2.可重定向文件(*.o)
可重定向文件又称为目标文件,它包含了代码和数据(这些数据是和其他重定位文件和共享的object文件一起连接时使用的)。
.o文件参与程序的连接(创建一个程序)和程序的执行(运行一个程序),它提供了一个方便有效的方法来用并行的视角看待文件的内容,这些.o文件的活动可以反映出不同的需要。
Linux下,我们可以用gcc -c编译源文件时可将其编译成*.o格式。
3.共享文件(*.so)
也称为动态库文件,它包含了代码和数据(这些数据是在连接时候被连接器ld和运行时动态连接器使用的)。动态连接器可能称为ld.so.1,libc.so.1或者 ld-linux.so.1。我的CentOS6.0系统中该文件为:/lib/ld-2.12.so
库是一种可执行代码的二进制格式,能够被载入到内存中执行,库分静态库和动态库两种:
静态库:这类库的名字一般是libxxx.a,xxx为库的名字。利用静态函数库编译成的文件比较大,因为整个函数库的所有数据都会被整合进目标代码中,他的优点就显而易见了,即编译后的执行程序不需要外部的函数库支持,因为所有使用的函数都已经被编译进去了。当然这也会成为他的缺点,因为如果静态函数库改变了,那么你的程序必须重新编译。
动态库:这类库的名字一般是libxxx.M.N.so,同样的xxx为库的名字,M是库的主版本号,N是库的副版本号。当然也可以不要版本号,但名字必须有。相对于静态函数库,动态函数库在编译的时候并没有被编译进目标代码中,你的程序执行到相关函数时才调用该函数库里的相应函数,因此动态函数库所产生的可执行文件比较小。由于函数库没有被整合进你的程序,而是程序运行时动态的申请并调用,所以程序的运行环境中必须提供相应的库。动态函数库的改变并不影响你的程序,所以动态函数库的升级比较方便。linux系统有几个重要的目录存放相应的函数库,如/lib /usr/lib。
当要使用静态的程序库时,连接器会找出程序所需的函数,然后将它们拷贝到执行文件,由于这种拷贝是完整的,所以一旦连接成功,静态程序库也就不再需要了。然而,对动态库而言,就不是这样。动态库会在执行程序内留下一个标记指明当程序执行时,首先必须载入这个库。由于动态库节省空间,linux下进行连接的缺省操作是首先连接动态库,也就是说,如果同时存在静态和动态库,不特别指定的话,将与动态库相连接。
静态库详解
1、静态库的命名格式
lib + 库的名字 + .a
例:libMyTest.a (MyTest为静态库的名字)
2、静态库作用分析
在项目开发过程中,经常出现优秀代码重用现象,又或者提供给第三方功能模块却又不想让其看到源代码,这些时候,通常的做法是将代码封装成库或者框架,生成的静态库要和头文件同时发布。
优点:
+ 寻址方便,速度快
+ 库在链接时被打包到可执行文件中,直接发布可执行程序即可以使用
缺点:
+ 静态库的代码被加载到可执行程序中,因此体积过大
+ 如果静态库的函数发生改变,必须重新编译可执行程序
3、静态库的制作与使用
1.准备两个源码文件st1.cpp和st2.cpp,用它们来制作库libmytest.a
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ cat st1.cpp
#include <iostream>
using namespace std;
void display1()
{
cout<<"This is my first static library!!!"<<endl;
}
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ cat st2.cpp
#include <iostream>
using namespace std;
void display2()
{
cout<<"This is my second static library"<<endl;
}
2.把两个源码文件生成目标文件
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ g++ -c st1.cpp st2.cpp
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ ll
总用量 24
drwxrwxr-x 2 xzj xzj 4096 7月 16 15:39 ./
drwxrwxr-x 7 xzj xzj 4096 7月 16 15:26 ../
-rw-rw-r-- 1 xzj xzj 115 7月 16 15:35 st1.cpp
-rw-rw-r-- 1 xzj xzj 2680 7月 16 15:39 st1.o
-rw-rw-r-- 1 xzj xzj 113 7月 16 15:35 st2.cpp
-rw-rw-r-- 1 xzj xzj 2680 7月 16 15:39 st2.o
3.使用ar -rsv libmytest.a st1.o st2.o制作静态库
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ ar -rsv libmytest.a st1.o st2.o
ar: 正在创建 libmytest.a
a - st1.o
a - st2.o
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ ll
总用量 32
drwxrwxr-x 2 xzj xzj 4096 7月 16 15:42 ./
drwxrwxr-x 7 xzj xzj 4096 7月 16 15:26 ../
-rw-rw-r-- 1 xzj xzj 5586 7月 16 15:42 libmytest.a
-rw-rw-r-- 1 xzj xzj 115 7月 16 15:35 st1.cpp
-rw-rw-r-- 1 xzj xzj 2680 7月 16 15:39 st1.o
-rw-rw-r-- 1 xzj xzj 113 7月 16 15:35 st2.cpp
-rw-rw-r-- 1 xzj xzj 2680 7月 16 15:39 st2.o
用file命令查看其属性,发现它确实是归档压缩文件
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ file libmytest.a
libmytest.a: current ar archive
用ar -t libmytest.a可以查看一个静态库包含了那些obj文件:
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ ar -t libmytest.a
st1.o
st2.o
4.写个测试程序来调用库libmytest.a中所提供的两个接口display1()和display2()。
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ cat main.cpp
void display1();
void display2();
int main()
{
display1();
display2();
return 0;
}
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ g++ -o run main.cpp -L./ -lmytest
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ ll
总用量 48
drwxrwxr-x 2 xzj xzj 4096 7月 16 15:54 ./
drwxrwxr-x 7 xzj xzj 4096 7月 16 15:26 ../
-rw-rw-r-- 1 xzj xzj 5586 7月 16 15:42 libmytest.a
-rw-rw-r-- 1 xzj xzj 95 7月 16 15:53 main.cpp
-rwxrwxr-x 1 xzj xzj 9424 7月 16 15:54 run*
-rw-rw-r-- 1 xzj xzj 115 7月 16 15:35 st1.cpp
-rw-rw-r-- 1 xzj xzj 2680 7月 16 15:39 st1.o
-rw-rw-r-- 1 xzj xzj 113 7月 16 15:35 st2.cpp
-rw-rw-r-- 1 xzj xzj 2680 7月 16 15:39 st2.o
结果调用成功:
xzj@xzj-VirtualBox:~/development_test/static_lib_test$ ./run
This is my first static library!!!
This is my second static library
动态库详解
动态库(共享库)
1、动态库的命名格式
lib + 库的名字 + .so
例:libMyTest.so (MyTest为动态库的名字)
2、动态库作用分析
共享库的代码是在可执行程序运行时才载入内存的,在编译过程中仅简单的引用,因此代码体积较小。
优点:
+ 节省内存
+ 易于更新,不用重新编译可执行程序,运行时自动加载
缺点:
+ 延时绑定,速度略慢
3、动态库的制作与使用
测试代码的目录结构与静态库相同。
第一步:生成与位置无关的.o文件
第二步:创建动态库
gcc -shared -o libMyTest.so *.o 参数:-shared 制作动态库 -o:重命名生成的新文件
mv libMyTest.so ../lib
第三步:使用动态库
第一种方法:
gcc + 源文件 + -L 动态库路径 + -l动态库名 + -I头文件目录 + -o 可执行文件名
gcc main.c -L lib -l MyTest -I include -o app
./app
(执行失败,找不到链接库,没有给动态链接器(ld-linux.so.2)指定好动态库 libmytest.so 的路径)
第二种方法:
gcc + 源文件 + -I头文件 + libxxx.so + -o 可执行文件名
gcc main.c -I include lib/libMyTest.so -o app
(执行成功,已经指明了动态库的路径)
1
2
3
4
5
6
7
8
9
10
如何解决第一种方法中找不到链接库的问题:
使用命令ldd app可以查看当前的链接库情况
第一种方法:
export LD_LIBRARY_PATH=自定义动态库的路径
(只能起到临时作用,关闭终端后失效)
LD_LIBRARY_PATH : 指定查找共享库(动态链接库)时除了默认路径之外的其他路径,该路径在默认路径之前查找
第二种方法:
将上述命令写入home目录下的.bashrc文件中,保存后重启终端生效(永久)
第三种方法:
直接将动态库拷贝到user/lib的系统目录下(强烈不推荐!!)
第四种方法:
将libmytest.so所在绝对路径追加入到/etc/ld.so.conf文件,使用sudo ldconfig -v 更新

浙公网安备 33010602011771号