Linux动态库与静态库
库的本质是将代码以二进制形式封装的可复用模块。
如:标准库函数 printf ,scanf的实现细节均被二进制封装,仅提供头文件接口声明,
有人就会说了平时的时候也没有链接,其实C/C++编译器在编译时会自动链接标准 C 库(libc),不需要我们手动链接。
有时候我们自己写了实现某功能代码,想给别人用,但是又不想让别人知道是怎么实现的,这个时候我们就可以把他封装成库。
常用的有静态库与动态库这俩类。
一、介绍
1、静态库
定义:由一组预编译的目标文件( .o 或 .obj )打包成的归档文件, 在程序的编译阶段就链接到了目标文件中。
格式:lib + 库名 + .a
优点:
1)无需外部依赖,可独立运行
2)库代码直接嵌入程序,运行效率更高
缺点:
3)库代码随程序多次加载,可执行文件体积大
3)需重新编译程序才以更新库
2、动态库
定义:动态库是独立的二进制文件,仅在程序运行时加载到内存,并与程序建立链接关系。
格式:lib + 库名 + .so
优点:
1)运行时动态加载,不需要嵌入可执行文件
2)更新时,只需要更新库,不需要重新编译
缺点:
3)首次加载的耗时长
4)缺失或版本不兼容的动态库会导致程序崩溃
二、创建动态库与静态库
基础文件
将以下面代码进行操作,总共三个文件,main.c,myfun.c,myfun.h。
main.c:
#include <stdio.h>
#include "myfun.h"int main()
{int x, y;printf("请输入第一个数:\r\n");scanf("%d",&x);printf("请输入第二个数:\r\n");scanf("%d",&y);my_func(x,y); /* 函数体的实现在 operation.c 中*/return 0;
}
myfun.c: 实现加减乘除,在主函数中调用,头文件中声明
#include "myfun.h"void my_func(int x,int y){printf("X + Y = %d\r\n",x+y);printf("X x Y = %d\r\n",x*y);printf("X - Y = %d\r\n",x-y);printf("X / Y = %f\r\n",(double)x/y);
}
myfun.c:
#ifndef __MYFUN_H
#define __MYFUN_H#include <stdio.h>void my_func(int x,int y);#endif
1、静态库的创建与使用
1.1 创建
分为两步:
1. 生成对应的目标文件 | gcc -o myfun.o -c myfun.c |
2. 通过目标文件生成静态库 | ar src libmyfun.a myfun.o |
-o: 目标文件名
-c:只编译,不链接
ar src:可通过目标文件生成静态库,想要深入了解可以自行搜索
1.2 使用
也分两步
1. 编译可执行文件 | gcc main.c -lmyfun -L./ |
2. 执行可执行文件 | ./a.out |
-l:表示需要链接的库,也就是我们刚刚生成的库名,进行掐头去尾:掐头:lib,去尾:.a,剩下的是库名
-L:表示库的搜索路径,如果不指定搜索路径的话就会跑到 /usr/lib 和 /usr/local/lib 下面去需要库,找不到就会报错( ./ 表示当前目录路径)
由于在编译的时候,库已经嵌入了程序中,所以执行时不用链接
2、动态库的创建与使用
2.1 创建
相比于静态库的生成,动态库只需要一步就好了:
1. 生成动态库 | gcc -fPIC -shared -o libmyfun.so myfun.c |
-fPIC:生成位置无关代码,确保动态库的代码段可在内存任意地址加载
-shared:编译器生成 动态链接库( .so 文件),而不是可执行文件
2.2 使用
三步:编译,设置环境变量,执行,不同的是动态库在编译和执行阶段都要链接动态库,是动态加载到程序中。
1. 编译 | gcc main.c -lmyfun -L./ |
2. 设置环境变量 | export LD_LIBRARY_PATH=./ |
3. 执行 | ./a.out -lmyfun |
编译阶段指定动态库路径
运行时加载动态库路径,所以即使指定动态库路径,还是会报错
export LD_LIBRARY_PATH=./ :将当前路径临时添加到搜索路径中。
默认的搜索路径为: /usr/lib 与 /etc/ld.so.conf,可将动态库移到第一个目录下,就可以省了第二步,并且不删除的话,是永久生效。