动态库和静态库

Posted 小倪同学 -_-

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态库和静态库相关的知识,希望对你有一定的参考价值。

文章目录

动静态库的基本原理

我们知道从源文件到可执行程序需要如下经历4个步骤

  1. 预处理: 完成头文件展开、去注释、宏替换、条件编译等,形成xxx.i文件。
  2. 编译: 完成词法分析、语法分析、语义分析、符号汇总等,检查无误后将代码翻译成汇编指令,形成xxx.s文件。
  3. 汇编: 将汇编指令转换成二进制指令,形成xxx.o文件。
  4. 链接: 将生成的各个xxx.o文件进行链接,形成可执行程序。

这里的动静态库本质上就是xxx.o文件的集合

初识动静态库

下面用一份简单的代码来认识动静态库

#include <stdio.h>

int main()

	printf("hello world\\n");
	return 0;

可以通过 ldd+文件名 来查看一个可执行程序所依赖的库文件。

使用 file+文件名 可以查看文件类型

图中 libc.so.6 表示c语言动态库,在Linux中生成的可执行程序默认情况下是动态库链接。

想要让生成的可执行程序为动态链接,需要添加 -static 选项。


从图中我们发现静态生成的可执行程序比动态生成的可执行程序要大得多

查看文件类型

静态链接生成的可执行程序并不依赖其他库文件,此时当我们使用ldd 文件名命令查看该可执行程序所依赖的库文件时就会看到以下信息。

动静态库各自特点

静态库:

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。

优点:

  • 不依赖库,库文件丢失对其没有影响

缺点:

  • 体积大,浪费资源(磁盘,内存)

动态库:

动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

优点:

  • 体积小,节省资源

缺点:

  • 依赖库,一旦库文件丢失,程序便无法运行

静态库的打包和使用

下面演示静态库的打包和使用,先创建两个源文件add.c,sub.c,再创建两个头文件add.h,sub.h。

add.h

#pragma once

extern int my_add(int x, int y);

add.c

#include "add.h"

int my_add(int x, int y)

	return x + y;

sub.h

#pragma once

extern int my_sub(int x, int y);

sub.c

#include "sub.h"

int my_sub(int x, int y)

	return x - y;

打包

  1. 让所有源文件生成对应的目标文件

  1. 使用ar命令将所有目标文件打包为静态库


ar命令是gnu的归档工具,常用于将目标文件打包为静态库,下面我们使用ar命令的-r选项和-c选项进行打包。

  • -r(replace):若静态库文件当中的目标文件有更新,则用新的目标文件替换旧的目标文件。
  • -c(create):建立静态库文件。
  1. 将头文件和生成的静态库组合

这里可以将头文件sub.h和add.h,静态库文件libmymath.a放入新建的目录lib下,此时可以将lib给别人使用了。

使用

下面尝试使用我们打包好的静态库文件lib

创建main.c文件

#include<stdio.h>
#include "add.h"
#include "sub.h"

int main()

      printf("%d\\n",my_add(10,20));
      printf("%d\\n",my_sub(10,20));

      return 0;

现在该目录下就只有main.c和我们刚才打包好的静态库。

此时使用gcc编译main.c生成可执行程序时需要携带三个选项:

  • -I:指定头文件搜索路径。
  • -L:指定库文件搜索路径。
  • -l:指明需要链接库文件路径下的哪一个库。

注意:

  1. 因为编译器不知道你所包含的头文件add.h在哪里,所以需要指定头文件的搜索路径。
  2. 因为头文件add.h当中只有my_add函数的声明,并没有该函数的定义,所以还需要指定所要链接库文件的搜索路径。
  3. 实际中,在库文件的lib目录下可能会有大量的库文件,因此我们需要指明需要链接库文件路径下的哪一个库。库文件名去掉前缀lib,再去掉后缀.so或者.a及其后面的版本号,剩下的就是这个库的名字。
  4. 我们也可以将头文件和库文件拷贝到系统路径下。

了解静态库的使用规则后,我们不禁会产生疑问,为什么之前使用gcc编译的时候没有指明过库名字?

因为我们使用gcc编译的是C语言,而gcc就是用来编译C程序的,所以gcc编译的时候默认就找的是C库,但此时我们要链接的是哪一个库编译器是不知道的,因此我们还是需要使用-l选项,指明需要链接库文件路径下的哪一个库。

实际上我们拷贝头文件和库文件到系统路径下的过程,就是安装库的过程。

动态库的打包与使用

我们还是用上面四个文件演示

这里我们使用Makefile文件

这里的makefile文件可以将要生成的文件一次性全部生成,如下图

注意:

  • shared: 表示生成共享库格式
  • fPIC:产生位置无关码

再将生成的头文件和动态库(libmymath.so)打包

使用

还是用刚才的main.c文件演示

用和静态库一样的方法生成可执行程序


运行一下,发现生成的可执行程序报错了

用ldd指令看看可执行程序所属

从图中可以看到,此时可执行程序所依赖的动态库是没有被找到的。

解决方法:

  1. 更改LD_LIBRARY_PATH

LD_LIBRARY_PATH是程序运行动态查找库时所要搜索的路径,我们只需将动态库所在的目录路径添加到LD_LIBRARY_PATH环境变量当中即可。

此时系统可以找到该可执行程序所依赖的动态库,程序也可以执行了。

  1. 配置/etc/ld.so.conf.d/

/etc/ld.so.conf.d/路径下存放的全部都是以.conf为后缀的配置文件,而这些配置文件当中存放的都是路径,系统会自动在/etc/ld.so.conf.d/路径下找所有配置文件里面的路径,之后就会在每个路径下查找你所需要的库。我们若是将自己库文件的路径也放到该路径下,那么当可执行程序运行时,系统就能够找到我们的库文件了。

在/etc/ld.so.conf.d/路径下新建一个以.conf为后缀的配置文件,将动态库的路径拷贝到该文件下

此时我们用ldd命令查看可执行程序时,发现系统还是没有找到该可执行程序所依赖的动态库。

我们需要使用ldconfig命令将配置文件更新一下

此时可以正常运行文件了

  1. 拷贝.so文件到系统共享库路径下

方法二和方法三不推荐,会污染路径。

以上是关于动态库和静态库的主要内容,如果未能解决你的问题,请参考以下文章

C语言里面的动态库和静态库

动态库和静态库的区别

Linux下的静态库和动态库

CMake 学习四:CMake 构建静态库和动态库

Linux下的动态库和静态库详解

CGO静态库和动态库