Linux下C/C++编程开发静态库和动态库
Posted MangataTS
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Linux下C/C++编程开发静态库和动态库相关的知识,希望对你有一定的参考价值。
文章目录
前言
下面的所有操作的的操作都是基于:
- 操作系统:
Ubuntu20.04.3-desktop
- GCC-Version:
gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
- G+±Version:
g++ (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0
一、库文件
- 库文件是计算机上的一类文件,可以简单的把库文件看成一种代码仓库,它提供给使用者一些可以直接拿来用的变量、函数或类。
- 库是特殊的一种程序,编写库的程序和编写一般的程序区别不大
- 库文件有两种,静态库 和 动态库(共享库),区别是:静态库在程序的链接阶段被复制到了程序中;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。
- 库的好处:代码保密 和 方便部署和分发
程序编译的过程:
二、静态库
2.1 命名规则
- 对于
Linux
平台的话,一般为:libxxx.a
lib
:固定前缀xxx
:自定义的名称.a
:固定后缀
例如有一个静态库的名称:libhello.a
- 对于
Windows
平台的话,一般为:libxxx.lib
lib
:固定前缀xxx
:自定义的名称.lib
:固定后缀
2.2 静态库的制作
静态库的制作过程:
制作静态库我们需要用到一个打包工具:ar
(archive)
ar
可以将一些 .o
文件打包成一个 .a
静态库
使用方法如下:
ar rcs libxxx.a xxx.o xxx.o……
这里我们举一个例子:
假设我们当前有三个文件:add.c
、plus.c
、head.h
然后我们先将这两个.c
文件编译、汇编成.o
文件,然后再使用ar
打包
gcc -c add.c plus.c
此时我们就会生成 add.o
和 plus.o
文件,然后我们使用ar
命令
ar rcs libcalc.a add.o plus.o
此时我们发现生成了一个 libcalc.a
的文件,注意的是静态库代码中使用到的头文件也需要一并发送,所以我们一般将头文件集中管理,于是创建一个include
文件夹
mkdir include
mv *.h include/
同样为了方便管理静态库文件,我们也创建一个lib
文件夹
mkdir lib
mv *.a lib/
此时我们的目录结构应该是这样的
2.3 静态库的使用
现在我们再新建一个main.c
文件,测试一下我们的这个静态库
touch main.c
然后写点简单的内容:
#include <stdio.h>
#include <head.h>
int main()
printf("%d\\n",add(1,2));
printf("%d\\n",multiply(3,4));
return 0;
然后我们再编译一下:
gcc -o main main.c -I ./include -l calc -L lib
其中-l
是指定链接库的名字,而-L
表示在哪个地方去找库,-I
表示的是头文件引用的位置
此时我们就生成了一个可执行程序main
,我们试着运行:./main
这就是一个简单的静态库的打包和使用啦~
三、动态库
3.1 命名规则
- 对于
Linux
平台的话,一般为:libxxx.so
lib
:固定前缀xxx
:自定义的名称.so
:固定后缀
例如有一个静态库的名称:libhello.a
- 对于
Windows
平台的话,一般为:libxxx.dll
lib
:固定前缀xxx
:自定义的名称.dll
:固定后缀
3.2 动态库的制作
动态库的制作过程:
有了上面静态库的学习,动态库的制作其实类似,只不过编译动态库的时候全程使用gcc
步骤主要分为两步:
- 通过
gcc
生成与位置无关的.o
文件
仍然使用上面的.c
文件(当然这里可以先将.o
文件都删掉:rm *.o
)加上-fpic
选项生成与位置无关的 .o
文件
为什么要用-fpic
呢?
因为
-fpic
用于编译阶段,产生的代码没有绝对地址,全部用相对地址,这正好满足了共享库的要求,共享库被加载时地址不是固定的。如果不加-fpic
,那么生成的代码就会与位置有关,当进程使用该.so
文件时都需要重定位,且会产生成该文件的副本,每个副本都不同,不同点取决于该文件代码段与数据段所映射内存的位置
gcc -c -fpic add.c plus.c -I include/
- 通过
gcc
生成动态库
gcc -shared add.o plus.o -o libcalc.so
此时我们就生成了一个libcalc.so
的动态库文件
3.3 动态库的使用
我们将 libcalc.so
移动到 lib
目录,并且删除libcalc.a
静态库
mv libcalc.so ./lib
rm ./lib/libcalc.a
此时我们的目录结构如下:
这个main
可执行文件是上面的,我们可以先将其删除:rm main
然后我们编译main.c
:
gcc -o main main.c -I include -L lib -l calc
此时我们又生成了main
的可执行文件
我们试着直接运行,会发现遇到这样的错误:
./main: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory
其实就是程序运行的时候并未加载到动态库,导致了运行出错
对于动态库而言,程序启动后,动态库会被动态加载到内存中,通过 ldd
(list dynamic dependencies
)命令检查动态库依赖关系
例如我们这里通过ldd main
来查看main
可执行文件的依赖动态库
我们发现此时的libcalc.so
是一个not found
的情况,所以我们需要在动态库的加载路径上将我们的libcalc.so
拷贝过去
首先我们要清楚,动态库如何定位到共享文件?
当系统加载可执行代码时候,能够知道其所依赖的库的名字,但是还需要知道绝对路
径。此时就需要系统的动态载入器来获取该绝对路径。对于elf
格式的可执行程序,是
由ld-linux.so
来完成的,它先后搜索elf
文件的 DT_RPATH
段 ——> 环境变量LD_LIBRARY_PATH
——> /etc/ld.so.cache
文件列表 ——> /lib/
,/usr/lib
目录找到库文件后将其载入内存。
流程图:
3.3.1 修改环境变量
新增一行数据:
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/mangata/Learn_C++/Linux_study/class2/lib
LD_LIBRARY_PATH
后面跟的是动态库的绝对路径
然后在终端输入source .bashrc
激活环境变量
然后此时我们通过ldd
查看main
就已经链接上了
3.3.2 修改/etc/ld.so.cache文件列表
因为该文件是一个二进制文件,不好直接更改,所以我们更改/ect/ld.so.conf
文件,在文件末尾加上lib
的路径
然后出来在终端输入sudo ldconfig
使其生效
3.3.3 /lib或/usr/lib目录
所以我们可以将libcalc.so
拷贝到/usr/lib
目录
sudo cp lib/libcalc.so /usr/lib
此时我们再来通过ldd
查看main
文件:
我们发现:libcalc.so => /lib/libcalc.so (0x00007effed09d000)
也就是说此时的动态库是能找到了
于是我们运行./main
四、动静态库的优缺点
4.1 静态库
优点:
- 静态库被打包到应用程序中加载速度快
- 发布程序无需提供静态库,移植方便
缺点:
- 消耗系统资源,浪费内存
- 更新、部署、发布麻烦
4.2 动态库
优点:
- 可以实现进程间资源共享(共享库)
- 更新、部署、发布简单
- 可以控制何时加载动态库
缺点:
- 加载速度比静态库慢
- 发布程序时需要提供依赖的动态库
以上是关于Linux下C/C++编程开发静态库和动态库的主要内容,如果未能解决你的问题,请参考以下文章
《Python开发 - Python杂记》Python与C/C++混合编程
《Python开发 - Python杂记》Python与C/C++混合编程