本文主要讲解 静态库 && 动态库的由来、区别,理解它存在的意义,感受C程序设计之美。
一、本文示例代码
新建staticlib文件夹,实现加减乘除c文件。目录结构及源码如下
├── add.c
├── divide.c
├── inc
│ └── head.h
├── minus.c
├── multi.c
└── test.c
程序代码如下:
// test.c
#include "head.h"
int main(int argc, char *argv[])
{
int a = 10; int b = 5;
printf("%d+%d=%d\n", a, b, add(a, b));
printf("%d-%d=%d\n", a, b, minus(a, b));
printf("%d/%d=%d\n", a, b, divide(a, b));
printf("%dx%d=%d\n", a, b, multi(a, b));
return 0;
}
头文件定义
// inc/head.h
#ifndef _HEAD_H_
#define _HEAD_H_
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <pthread.h>
int add(int , int );
int minus(int , int );
int divide(int , int );
int multi(int , int );
#endif
加减乘法实现
// add.c
int add(int a, int b)
{
return a+b;
}
// divide
int divide(int a, int b)
{
return a/b/2;
}
// minus
int minus(int a, int b)
{
return a-b;
}
// multi
int multi(int a, int b)
{
return a*b;
}
二、静态库的制作与使用
2.1、将 .c 生成 .o 文件
gcc -c add.c -o add.o
这里将加减乘除合起来写成一句
gcc -c add.c && gcc -c divide.c && gcc -c minus.c && gcc -c multi.c
2.2、使用 ar 工具制作静态库
ar rcs libmylib.a add.o minus.o multi.o divide.o
注意:静态库名字要以
lib
开头,以.a
结尾,例如:libmylib.a
运行后,生成libmylib.a静态库文件:
2.3、编译静态库到可执行文件中
这里可执行文件简单取名为a.out
-I:为引入头文件
gcc test.c libmylib.a -o a.out -I./inc
2.4、运行可执行程序
./a.out
三、动态库的制作与使用
新建dynamiclib目录,实例程序还是一样,参考[一、本文示例代码](# 一、本文示例代码)
3.1、生成位置无关的.o文件
-fPIC:使用这个参数过后,生成的函数就和位置无关,挂上@plt标识,等待动态绑定
$ gcc -c add.c -o add.o -fPIC
这里将加减乘除合起来写成一句
gcc -c add.c -fPIC && gcc -c divide.c -fPIC && gcc -c minus.c -fPIC && gcc -c multi.c -fPIC
3.2、使用 gcc -shared制作动态库
理解shared: 动态库又叫做shared动态共享库
gcc -shared -o libmylib.so add.o minus.o multi.o divide.o
生成动态库libmylib.so
我们一般会把动态库放到lib文件夹中
mkdir lib
mv libmylib.so ./lib
3.3、编译动态库到可执行文件中
编译可执行程序时指定所使用的动态库。
-l:指定库名 -L:指定库路径。(注意这里字母是:小l和大l)
gcc test.c -o a.out -l mylib -L ./lib -I./inc
3.4、运行可执行程序
$ ./a.out
3.5、动态库加载错误原因及解决方式
回顾:对于动态库的使用,我们在安卓中会有System.load操作
System.load("mylib");
那么在linux中,我们同样需要分析:
出错原因 | 分析 |
---|---|
连接器 | 工作于链接阶段,工作时需要 -l 和 -L |
动态链接器 | 工作于程序运行阶段,工作时需要提供动态库所在目录位置 |
解决方式:
解决方案一:export临时环境变量
注意:只是临时生效, 终端重启环境变量失效
export LD_LIBRARY_PATH=动态库路径
pwd查看当前目录,然后目录指定到lib
export LD_LIBRARY_PATH=/root/gccTest/dynamiclib/lib
可以看到运行成功
解决方案二:环境变量/etc/profile
# 修改环境变量方式1
sudo vim /etc/profile
# 生效命令(也可重启终端)
source /etc/profile
重启终端或者运行source命令后生效
解决方案三:环境变量.bash_profile
# 修改环境变量方式2
sudo vim ~/.bash_profile
# 生效命令(也可重启终端)
source ~/.bash_profile
解决方案四:so库加载路径
sudo vim /etc/ld.so.conf
然后添加上so库文件路径如下
使配置生效
sudo ldconfig -v
然后在运行程序,即可正常使用
./a.out
解决方案五:拷贝so库到系统lib目录
这里的/root/gccTest/dynamiclib/lib/libmylib.so填写你自己的
cp /root/gccTest/dynamiclib/lib/libmylib.so /lib
3.6、查看链接库
ldd是一个脚本,方便查看链接库
ldd a.out
四、 动态库和静态库对比
4.1、静态库特点总结
- 在链接阶段,会将汇编生成的目标文件.o与引用到的库一起链接打包到可执行文件中。
- 静态库对函数库的链接是放在编译时期完成的,程序在运行时与函数库再无瓜葛,移植方便。
- 浪费空间和资源,因为所有相关的目标文件与牵涉到的函数库被链接合成一个可执行文件。
4.2、动态库特点总结
- 动态库把对一些库函数的链接载入推迟到程序运行的时期。
- 可以实现进程之间的资源共享。(因此动态库也称为共享库)
- 将一些程序升级变得简单,甚至可以真正做到链接载入完全由程序员在程序代码中控制(显示调用)。