1、静态库动态库介绍

静态库是指在我们的应用中,有一些公共代码是需要反复使用,就把这些代码编译为“库”文件;在链接步骤中,连接器将从库文件取得所需的代码,复制到生成的可执行文件中的这种库。

程序编译一般需经预处理、编译、汇编和链接几个步骤。静态库特点是可执行文件中包含了库代码的一份完整拷贝;缺点就是被多次使用就会有多份冗余拷贝。

静态库和动态库是两种共享程序代码的方式,它们的区别是:静态库在程序的链接阶段被复制到了程序中,和程序运行的时候没有关系;动态库在链接阶段没有被复制到程序中,而是程序在运行时由系统动态加载到内存中供程序调用。使用动态库的优点是系统只需载入一次动态库,不同的程序可以得到内存中相同的动态库的副本,因此节省了很多内存。

静态库在文件中静态展开,所以有多少文件就展开多少次,非常吃内存,但是这样的好处就是静态加载的速度快

使用动态库会将动态库加载到内存,10个文件也只需要加载一次,然后这些文件用到库的时候临时去加载,速度慢一些,但是很省内存

动态库和静态库各有优劣,根据实际情况合理选用即可。

2、静态库制作

静态库名字以lib开头,以.a结尾

例如:libmylib.a

静态库生成指令

ar rcs 库名字 原材料

ar rcs libmylib.a file1.o

步骤一

写好源代码

三个文件:add.c、sub.c、div1.c(加法、减法、除法)

int add (int a, int b) {
        return a + b;
}
int sub (int a, int b) {
        return a - b;
}
int div1 (int a, int b) {
        return a / b;
}

test.c文件

/*************************************************************************
 > File Name: test.c
 > Author: Winter
 > Created Time: 2021年09月06日 星期一 20时21分35秒
 ************************************************************************/

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<pthread.h>

int main(int argc, char* argv[])
{
	int a = 10, b = 5;
	printf("%d + %d = %d \n", a, b, add(a, b)); 
	printf("%d - %d = %d \n", a, b, sub(a, b)); 
	printf("%d / %d = %d \n", a, b, div1(a, b)); 
	return 0;
}

步骤二

编译源代码生成.o文件

gcc -c add.c -o add.o
gcc -c sub.c -o sub.o
gcc -c div1.c -o div1.o

步骤三

制作静态库

ar rcs libMyMath.a add.o sub.o div1.o

静态库的使用:

gcc test.c libMyMath.a -o test

上图中,test.c直接使用了mymath库中的add,sub,div1函数,所以在编译时要导入静态库

编译时出现了函数未定义的警告,可以忽略,让系统生成默认的定义。

出现的警告,用编译器隐式声明来解决的

编译器只能隐式声明返回值为int的函数形式:int add(int ,int )

如果函数不是返回的int,则隐式声明失效,会报错。

在test.c中加入函数声明:

 再次进行编译,就不会报错了:

 上面这个方法,需要库的使用者知道库里的函数,完事儿一个一个加到代码里,不太行。

一般静态库也会有相应的头文件。

下面使用头文件的方法来加载静态库

#ifndef _MYMATH_H_
#define _MYMATH_H_

int add (int,int);
int sub(int,int);
int div1(int,int);


#endif

ifndef、define为头文件守卫,防止在代码中多次include头文件,多次展开静态库,带来的额外开销,这样也不会报错了,而且更加科学。

再在test.c中加入头文件。

下面将静态库和头文件分别放至其他目录下

新建resource、inc、lib目录。

将*.c(test.c除外。图片有误)、*.o放到resource目录下;将*.h(头文件)放到inc;将*.a(静态库)放到lib目录下

运行过程如下

最终目录如下

 接下来重新编译 

gcc test.c ./lib/libMyMath.a -o test -I ./inc/

 -I: 指定头文件所在目录位置。

3、动态库制作

写在源代码里的函数,相对main函数偏移是一定的,链接时,回填main函数地址之后,其他源代码里的函数也就得到了地址。

动态库里的函数会用一个@plt来标识,当动态库加载到内存时,再用加载进去的地址将@plt替换掉。

查看反汇编

objdump -dS test > out
vi out

printf是动态库函数,依赖@plt。回填的是函数指针。

【】

制作动态库的步骤

步骤一:生成位置无关的.o文件

*.c文件的内容和静态库里面的内容一样。

 步骤二:制作动态库

gcc -shared -o lib库名.so add.o sub.o div.o

 库名:libMyMath.so。【lib是固定的,so是固定的,中间的MyMath是自己定义的】

将生成的动态库libMyMath.so放到新创建的lib路径下。

 将MyMath.h头文件放到inc目录下。

 步骤三:编译程序

文件分布如下:动态库在lib目录下,头文件在inc目录下

 下面编译文件

gcc test.c -o test -l MyMath -L ./lib/ -I ./inc/

 MyMath是动态库的名称(去掉lib,去掉.so);

-l: 指定库名

-L 指定动态库路径

-I 指定头文件

步骤四:执行文件,出错

 步骤五

出错原因分析:

连接器: 工作于链接阶段,a.out生成前使用,工作时需要 -l 和 -L

动态链接器: 工作于程序运行阶段,工作时需要提供动态库所在目录位置【位置固定】

法一:通过环境变量

指定动态库路径并使其生效,然后再执行文件

通过环境变量指定动态库所在位置:export LD_LIBRARY_PATH=动态库路径

 当关闭终端,再次执行a.out时,又报错。

这是因为,环境变量是进程的概念,关闭终端之后再打开,是两个进程,环境变量发生了变化。

法二:永久生效

写入 终端配置文件。  修改.bashrc  【建议使用绝对路径】

1) vi ~/.bashrc

2) 写入 export LD_LIBRARY_PATH=动态库路径  保存

 3). .bashrc/  source .bashrc / 重启 终端  ---> 让修改后的.bashrc生效

4)./a.out 成功!!!

 查看动态库的位置

 法三:拷贝自定义动态库 到 /lib (标准C库所在目录位置)

放到 /lib或者/lib/x86_64-linux-gnu下

再把~/.bashrc的配置注释掉

查看动态库位置

 法四: 配置文件法

1)sudo vi /etc/ld.so.conf

2)写入 动态库绝对路径  保存

3)sudo ldconfig -v  使配置文件生效。

4)./a.out 成功!!!--- 使用 ldd  a.out 查看

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐