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.cplus.chead.h

然后我们先将这两个.c文件编译、汇编成.o 文件,然后再使用ar 打包

gcc -c add.c plus.c

此时我们就会生成 add.oplus.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

其实就是程序运行的时候并未加载到动态库,导致了运行出错

对于动态库而言,程序启动后,动态库会被动态加载到内存中,通过 lddlist 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++编程开发静态库和动态库的主要内容,如果未能解决你的问题,请参考以下文章

Linux下C/C++编程开发静态库和动态库

《Python开发 - Python杂记》Python与C/C++混合编程

《Python开发 - Python杂记》Python与C/C++混合编程

linux下intel的mkl编程代码,怎么样编译。C++和C语言代码

C语言Linux下动态库和静态库详解

Linux下gcc生成和使用静态库和动态库详解