文章目录
动态链接库可以简单这么理解:
有两个工程:
- 【工程1】专门用来生成.so链接库文件,生成库以后只需要保留libxxx.so库文件即可,其他文件均可以不需要。
- 【工程2】为实际项目工程,需要使用上面的so库。
下面以一个简单的加减乘除demo为例记录生成、使用动态链接库的方法。
1、工程1:生成动态链接库【libcaculate.so】
生成链接库的代码实际上跟最后工程的代码没有关系,该步骤的目的仅仅是生成动态链接库,之后将so库文件和接口声明.h文件提供给别人,别人就可以使用了。
1.1 代码
-
工程目录结构:
code_delivery_lib_so ├── caculate.c ├── caculate.h └── Readme.txt
-
caculate.c
#include <stdio.h> #include <string.h> //加法 int add(int a, int b) { return ((a) + (b)); } //减法 int sub(int a, int b) { return ((a) - (b)); } //除法 int div(int a, int b) { return (int)((a) / (b)); } //乘法 int mul(int a, int b) { return ((a) * (b)); }
-
caculate.h
该文件作为so库对外接口函数声明文件,里面包含了库中所有可供调用的函数声明。
备注:事实上该文件作为对外接口声明文件,代码结构中是可有可无的。其本质意义其实更像是一个Readme文件,告诉使用so库的人:我都给你提供了什么函数。你要用哪些函数,你就新建一个.h文件,声明一下这些函数,或者直接用我这个.h文件。
#ifndef CACULATE_H #define CACULATE_H //加法 int add(int a, int b); //减法 int sub(int a, int b); //除法 int div(int a, int b); //乘法 int mul(int a, int b); #endif
-
Readme.txt
生成动态链接库命令: gcc -shared -fPIC caculate.c -o libcaculate.so 其中: -shared -fPIC 参数指明要生成链接库文件 -o 参数指定生成的链接库的名称 注意: libcaculate.so 固定格式链接库名称为:libxxx.so,前缀lib,后缀.so,系统会自动识别名称xxx 本例子中,库名称为caculate,使用链接库命令的时候会用该名称
## 1.2 终端执行命令
-
gcc -shared -fPIC caculate.c -o libcaculate.so
可以看到当前文件夹下生成了一个libcaculate.so文件。
项目开发时,提供给别人【libcaculate.so】和【caculate.h】文件就可以了。
2、工程2:项目工程(使用动态链接库)
2.1 代码
-
工程目录结构:
project ├── lib │ ├── caculate.h │ └── libcaculate.so ├── main.c └── Readme.txt
-
caculate.h
该文件作用是声明so库文件中可调用的函数。没有该文件的话,编译会报错。
#ifndef CACULATE_H #define CACULATE_H //加法 int add(int a, int b); //减法 int sub(int a, int b); //除法 int div(int a, int b); //乘法 int mul(int a, int b); #endif
-
main.c
#include <stdio.h> #include <string.h> #include "./lib/caculate.h" //引用对应库的头文件,注意路径 int main(int argc, char *argv[]) { int a = 5; int b = 8; printf("a + b = %d\r\n", add(a, b)); printf("a - b = %d\r\n", sub(a, b)); printf("a * b = %d\r\n", mul(a, b)); printf("a / b = %d\r\n", div(a, b)); return 0; }
-
Readme.txt
使用动态链接库命令: gcc main.c -o main -L ./lib/ -lcaculate -Wl,-rpath,$(pwd)/lib/ 其中: -L 指定链接库的路径 -l 后面跟链接库的名称,要省略前缀lib和后缀.so,编译器会自动解析加上前后缀的 -Wl,-rpath 指定环境变量库路径,$(pwd)/lib/表示当前目录下的lib目录
2.2 终端执行命令
-
gcc main.c -o main.out -L ./lib/ -lcaculate -Wl,-rpath,$(pwd)/lib/
可以看到目录下生成一个可执行文件【main.out】
2.3 结果展示
-
执行main.out
lsy@ubuntu18:~/practice/c/lib_so_test/test/project$ ./main.out a + b = 13 a - b = -3 a * b = 40 a / b = 0
3、学习动态链接库时遇到的坑
3.1 接口声明文件xxx.h
我们要用库文件中的函数,我们就需要自己新建一个xxx.h文件声明这些函数,或者直接copy一份提供so库文件的人员给我们提供的.h文件。然后再对应.c文件中加上头文件引用:#include “xxx.h”。最后进行工程编译。
否则会报错函数未定义,或者找不到xxx.h文件。
简单概括,步骤如下:
使用so库,代码注意事项步骤:
- 拿到so库
- 写一个xxx.h文件,声明我们即将调用的函数(或者可以直接使用提供.so库人员给我们的.h声明文件)
- 对应.c文件中加上头文件引用:#include “xxx.h”
3.2 环境变量LD_LIBRARY_PATH的设置
有时候链接库的时候,会提示找不到库,报错,因为系统环境变量会默认找LD_LIBRARY_PATH所指向的路径,此时可以临时这是环境变量LD_LIBRARY_PATH的值,也可以将libxxx.so拖到usr/lib下面,也可以用另一种方法,即编译命令中直接加上参数,指定库路径,这样写Makefile的时候也会非常方便。
即:
使用动态链接库命令:
gcc main.c -o main -L ./lib/ -lcaculate -Wl,-rpath,$(pwd)/lib/
中的-Wl,-rpath 这两个参数,具体含义如下:
-Wl,-rpath 指定环境变量库路径,$(pwd)/lib/表示当前目录下的lib目录