前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C语言 | 什么是静态链接库和动态链接库?

C语言 | 什么是静态链接库和动态链接库?

作者头像
飞哥
发布2020-08-11 17:00:28
4.2K0
发布2020-08-11 17:00:28
举报

今天分享的是静态链接库和动态链接库的相关知识,并且自己动手写一个简单的静态函数库和动态函数库,来体验这个流程。

首先要从函数库的发明开始说起。

我们在写C语言代码的时候,一般都是先写

代码语言:javascript
复制
#include <stdio.h>

这是一个标准输入输出的头文件,因为我们可能要用到像printf这类的函数,而这类函数就是包含在这个头文件当中。但是为什么包含这个头文件就可以使用里面的函数呢?

早期,程序员写代码都是从0开始写的,后来慢慢的发现,有些功能的函数会被反复使用到,如果大家写代码都要从头开始写就非常浪费时间,于是程序员们会经常在一起交流,看一下大家写了什么好代码,可以相互借鉴,把那些通用的代码保留起来,下次就可以直接用了。

刚开始是直接交换源文件的形式,就是我把我写好的.c文件给你,你把你写好的.c文件给我,这样的交换非常彻底,因为我可以通过源文件知道你函数具体是怎么实现的。但是这种方式有它的缺点,缺点就是无法以商业化形式来发布函数库。

商业公司需要将自己的有用的函数库共享给别人(当然是付费的),但是又不能给客户源代码。这时候的解决方案就是以库(主要有2种:静态库和动态库)的形式来提供。

比较早出现的是静态链接库。静态库其实就是商业公司将自己的函数库源代码经过只编译不链接形成.o的目标文件,然后用ar工具将.o文件归档成.a的归档文件(.a的归档文件又叫静态链接库文件)。商业公司通过发布.a库文件和.h头文件来提供静态库给客户使用。动态链接库比静态链接库出现的晚一些,效率更高一些,是改进型的。现在我们一般都是使用动态库。

静态库与动态库的区别就是,静态库在用户链接自己的可执行程序时就已经把调用的库中的函数的代码段链接进最终可执行程序中了,相当于是把别人的代码拷贝了一份,哪里有调用,哪里就要拷贝,这样程序是很完整的,但是有个缺点,就是非常占内存,如果反复调用一个函数,就要拷贝很多份相同的代码。

而动态库不一样,动态链接库本身不将库函数的代码段链接入可执行程序,只是做个标记。然后当应用程序在内存中执行时,运行时环境发现它调用了一个动态库中的库函数时,会去加载这个动态库到内存中,然后以后不管有多少个应用程序去调用这个库中的函数都会跳转到第一次加载的地方去执行(不会重复加载)。

gcc中编译链接程序默认是使用动态库的,要想静态链接需要显式用

代码语言:javascript
复制
-static

来强制静态链接。

上面讲的都是链接库的背景知识,接下来自己制作一个简单的链接库并且使用。

  1. 制作静态链接库

首先准备我们的源文件和头文件,因为只是简单示例,所以代码不需要写太复杂。

代码语言:javascript
复制
//源文件
#include <stdio.h>
#include "test.h"

int add(int a,int b)
{
  return a+b;
}
代码语言:javascript
复制
//头文件
#ifndef __test_H
#define __test_H

int add(int a,int b);

#endif

接下来使用gcc -c只编译不连接,生成.o文件;然后使用ar工具进行打包成.a归档文件库名不能随便乱起,一般是lib+库名称,后缀名是.a表示是一个归档文件。这里我们通过Makefile来实现。

代码语言:javascript
复制
all:
  gcc main.c -o main -ltest -L.

lib: 
  gcc -o test.o test.c -c
  ar -rc libtest.a test.o

clean:
  rm main
cleanall:
  rm main
  rm *.o *.a

这里的lib目标里面的两行代码就是生成静态库的代码。所以我们可以使用

代码语言:javascript
复制
make lib

命令来生成静态库。

第一句由test.c生成test.o,第二句由test.o生成libtest.a文件。这个.a文件就是静态库文件,lib后面的是库文件名称。制作出来了静态库之后,发布时只需要发布.a文件和.h文件。以上就是静态链接库的制作过程。

假设我们已经拿到了别人发布的.a和.h文件,那么要怎么使用呢?这里先写一个测试代码:

代码语言:javascript
复制
#include <stdio.h>
#include "test.h"
int main(void)
{
  int a;
  a=add(3,8);
  printf("a=%d\n",a);
  return 0;
}

首先肯定要在测试代码中包含相应的头文件才能调用函数。正常来讲,上面的代码应该可以直接编译运行了,我们来试一下

代码语言:javascript
复制
gcc main.c

结果发现会报链接错误。这是因为编译器只会去链接几个常用的库,如果要链接其他的库,必须使用-l指令。所以我们再试一次

代码语言:javascript
复制
gcc main.c -ltest

这个时候还是有问题,提示找不到,这是因为这个库是我们自己写的,编译器并不知道去哪里找,所以,还应该指定库的路径

代码语言:javascript
复制
gcc main.c -o jintai -ltest -L. -static

-L. 就是指定链接到当前路径,这个时候我们发现已经可以正常编译程序,并且可以生成可执行文件,执行结果也是正确的。

以上就是静态链接库的制作及使用方法。

ii.制作动态链接库

制作动态链接库的编译指令要稍作修改

代码语言:javascript
复制
lib: 
  gcc test.c -o test.o -c -fPIC
  gcc -o libaston.so test.o -shared 

-fPIC是位置无关码,-shared是按照共享库的方式来链接。

编译完之后可以得到一个.so文件,这个文件就是动态链接库文件。做库的人给用库的人发布库时,发布libxxx.so和xxx.h即可。

对于动态链接库,该怎么使用呢?

我们直接使用

代码语言:javascript
复制
gcc main.c -o dongtai

会发现报错,错误和静态差不多。经过测试会发现,也必须使用

代码语言:javascript
复制
gcc main.c -o dongtai -ltestso -L.

指令编译才成功,也就是说要指定链接库和这个库的路径。

但是我们一执行还是发现会报错

./dongtai: error while loading shared libraries: libtestso.so: cannot open shared object file: No such file or directory

这是因为动态链接库运行时需要被加载(运行时环境在执行程序的时候发现他动态链接了libtestso.so,于是乎会去固定目录尝试加载libaston.so,如果加载失败则会打印以上错误信息。)

这就是动态和静态的一个区别,静态库在编译完没问题之后就可以直接运行,因为他生成的可执行文件是完整的,但是动态库不行,他必须要去固定的目录(其实就是环境变量)加载内容到内存中。

该怎么解决呢?两种常用方法:

1、将libtestso.so放到固定目录下就可以了,这个固定目录一般是/usr/lib目录。

代码语言:javascript
复制
 sudo cp libtestso.so /usr/lib

复制过去之后就可以正常执行了。

2、使用环境变量LD_LIBRARY_PATH。操作系统在加载固定目录/usr/lib之前,会先去LD_LIBRARY_PATH这个环境变量所指定的目录下去寻找,如果找到就不用去/usr/lib下面找了,如果没找到再去/usr/lib下面找。所以解决方案就是将libaston.so所在的目录导出到环境变量LD_LIBRARY_PATH中即可。

代码语言:javascript
复制
 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/mnt/d/test

冒号后面的是libaston.so所在的路径。通过这个方法也可以正常执行。

以上就是今天的内容,主要是介绍了函数库的来源和背景知识,以及静态链接库和动态链接库的制作及使用方法。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-08-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 电子技术研习社 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档