C语言的静态库和的动态库
最佳答案 问答题库598位专家为你答疑解惑
C语言的静态库和的动态库
编写函数并编译
-
创建
calc.c
文件#include "calc.h"int add(int a, int b) {return a + b; }int sub(int a, int b) {return a - b; }
-
创建
calc.h
文件// 计算功能的头文件 #ifndef __CALC_H #define __CALC_Hint add(int, int); int sub(int, int);#endif
-
创建
show.c
文件#include <stdio.h> #include "show.h"void show(int l, char op, int r, int res) {printf("%d %c %d = %d\n", l, op, r, res); }
-
创建
show.h
文件// 输出函数的头文件 #ifndef __SHOW_H #define __SHOW_Hvoid show(int, char, int, int);#endif
对代码只进行编译而不进行链接:
gcc -c calc.c
、gcc -c show
-
编写接口文件
math.h
// 接口文件 #ifndef __MATH_H #define __MATH_H#include "calc.h" #include "show.c"#endif
制作静态库
ar
命令ar [选项] <静态库文件> <目标文件列表>
- 有以下几个选项
-r
将目标插入到静态库中,已存在则更新-q
将目标文件追加到静态库尾-d
从静态库中删除目标文件-t
列表显示静态库中的目标文件-x
将静态库展开为目标文件
使用这个命令制作静态库:ar -r libmath.a calc.o show.o
使用静态库
-
编写主函数
main.c
#include "math.h"int main(void) {int a = 100, b = 200;show(a, '+', b, add(a, b)); // 使用add和showshow(a, '-', b, sub(a, b)); // 使用sub和showreturn 0; }
-
编译的时候一共有三个方式(linux中指定库名的时候默认将)
-
直接链接静态库:
gcc main.c libmath.a -o test
-
用l指定库名,用-L指定库路径(因为我这个是在当前路径.):
gcc main.c -lmath -L. -o test
-
将库路径添加到
LIBRARY_PATH
中,然后使用-l
指定库名export LIBRARY_PATH=$LIBRARY_PATH:/home/bhlu/study/static gcc main.c -lmath -o test
-
-
直接执行:
./test
整个静态库制作和使用的顺序
- 编辑库的实现代码和接口文件
- 计算模块:
calc.h
、calc.c
- 显示模块:
show.h
、show.h
- 接口文件:
math.h
- 计算模块:
- 编译成目标文件
gcc -c calc.c
gcc -c show.c
- 打包成静态库
ar -r libmath.a calc.o show.o
- 编译
gcc main.c -lmath -L. -o test
- 使用
./test
动态库的制作和使用
动态库和静态库不同,链接动态库不需要将被调用的函数代码复制包含调用代码的可执行文件中,相反链接器会在调用语句处嵌入一段指令,在该程序执行时,会加载该动态库并寻找被调用函数的入口地址并执行;动态库的扩展名是.so
编写函数并编译
动态库的函数创建和静态库一致
-
创建
calc.c
文件#include "calc.h"int add(int a, int b) {return a + b; }int sub(int a, int b) {return a - b; }
-
创建
calc.h
文件// 计算功能的头文件 #ifndef __CALC_H #define __CALC_Hint add(int, int); int sub(int, int);#endif
-
创建
show.c
文件#include <stdio.h> #include "show.h"void show(int l, char op, int r, int res) {printf("%d %c %d = %d\n", l, op, r, res); }
-
创建
show.h
文件// 输出函数的头文件 #ifndef __SHOW_H #define __SHOW_Hvoid show(int, char, int, int);#endif
对代码只进行编译而不进行链接:
gcc -c calc.c
、gcc -c show
-
编写接口文件
math.h
// 接口文件 #ifndef __MATH_H #define __MATH_H#include "calc.h" #include "show.c"#endif
制作动态库
- 使用这个命令制作动态库:
gcc -shared -fpic cala.o show.o -o libmath.so
- 其实也可以使用这个命令一次性完成:
gcc -shared -fpic cala.c show.c -o libmath.so
-fPIC
:大模式,生成代码比较大,运行速度比较慢,所有平台都支持-fpic
:小模式,生成代码比较小,运行速度比较快,仅部分平台支持
使用动态库
-
编写库的使用代码
main.c
#include "math.h"int main(void) {int a = 100, b = 200;show(a, '+', b, add(a, b)); // 使用add和showshow(a, '-', b, sub(a, b)); // 使用sub和showreturn 0; }
-
编译并链接动态库(这里编译也是有三种方式,与上面静态库一致)
-
直接链接动态库:
gcc main.c libmath.so -o test
-
用l指定库名,用-L指定库路径(因为我这个是在当前路径.):
gcc main.c -lmath -L. -o test
-
将库路径添加到
LIBRARY_PATH
中,然后使用-l
指定库名export LIBRARY_PATH=$LIBRARY_PATH:/home/bhlu/study/shared gcc main.c -lmath -o test
-
-
编译成功后,在执行程序之前,这里必须配置链接器的环境变量,不然会报以下错误
# error while loading shared libraries: libmath.so: cannot open shared object file: No such file or directory# 需要配置一下链接器到的环境变量 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/bhlu/study/shared
-
执行命令
./test
整个动态库制作和使用的顺序
- 编辑库的实现代码和接口文件
- 计算模块:
calc.h
、calc.c
- 显示模块:
show.h
、show.h
- 接口文件:
math.h
- 计算模块:
- 编译成目标文件
gcc -c calc.c
gcc -c show.c
- 打包成动态库
gcc -shared -fpic calc.o show.o -o libmath.so
- 编译
gcc main.c -lmath -L. -o test
- 配置链接器的环境变量
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/bhlu/study/shared
- 使用
./test
动态库的动态加载
- 动态库(也称为共享库或动态链接库)是一种在程序运行时加载的可执行代码和数据的集合。动态加载指的是在程序运行时根据需要加载和链接动态库,而不是在编译时将其静态链接到可执行文件中。
- 以下是一些动态加载的主要原因:
- 节省内存:动态库允许多个程序共享同一份代码和数据,当多个程序使用同一个动态库时,只需在内存中加载一次即可。
- 灵活性:动态加载允许程序在运行时根据需要加载额外的功能,在不停止程序的情况下可以添加和移除一些功能。
- 易于更新和维护:修复漏洞不需要重新编译而去调整整个程序。
- 增加代码的复用性:当多个程序使用同一个库的函数时,这样可以减少代码的冗余。
在使用动态库的动态加载时,为了避免后面这种情况:假设一个程序执行了10分钟,而它用到动态库的地方就只需1分钟,为了实现动态库使用完就立即回收,从而减少内存的占用,我们采用了下面的这个操作方法。
-
load.c
(里面用的到的函数在代码后面会进行解释)#include <stdio.h> #include <dlfcn.h>int main(void) {// 1. 将动态库载入内存-dlopen函数void *handle = dlopen("/home/bhlu/study/shared/libmath.so", RTLD_NOW);if(handle == NULL){// 当载入不成功的时候fprintf(stderr, "dlopen: %s\n", dlerror());return -1;}// 2. 获取库中函数的地址-dlsym函数int (*add)(int, int) = dlsym(handle, "add");if(add == NULL){fprintf(stderr, "dlsym: %s\n", dlerror());return -1;}int (*sub)(int, int) = dlsym(handle, "sub");if(sub == NULL){fprintf(stderr, "dlsym: %s\n", dlerror());return -1;}int (*show)(int, char, int, int) = dlsym(handle, "show");if(show == NULL){fprintf(stderr, "dlsym: %s\n", dlerror());return -1;}// 3. 使用int a = 100, b = 200;show(a, '+', b, add(a, b));show(a, '-', b, sub(a, b));// 4. 卸载动态库// dlclose(handle)if(dlclose(handle)){// 判断是否卸载成功,其实当有其他程序再调用这个动态库的时候,是卸载不了的fprintf(stderr, "dlclose: %s\n", dlerror());return -1;}return 0; }
-
使用相关命令编译
load.c
:gcc load.c -ldl -o load
,因为这里用到了libld这个库,所以编译的时候需要指定一下,不过我在我的ubuntu环境下不加这个也可以,等之后有时间了解一下原因。
相关函数的介绍
- 使用动态加载的函数,需要加载
dlfcn.h
这个头文件:#include <dlfcn.h>
dlopen
函数void *dlopen(char const *filename, int flag);
- 功能:将动态库载人内存并获得其访问句柄
- 参数
filename
:动态库路径,如果只给文件名,会根据LD_LIBRARY_PATH
环境变量去搜索,上面示例给的是绝对路径flag
:加载方式RTLD_LAZY
:延时加载,使用动态库中的符号时才会真的加载进内存RTLD_NOW
:立即加载
- 返回值:成功就返回动态库的访问句柄,用于后续函数的调用,失败就返回NULL
dlsym
函数void *dlsym(void *handle, char const *symbol);
- 功能:从已加载的动态库中获取指定名称的函数地址
- 参数
handle
:动态库访问句柄,即dlopen
函数的返回值symbol
:符号名,即函数名
- 返回值:成功返回函数的地址,失败返回NULL(因为函数返回值是void*类型,所以定义返回值的时候,需要造型跟实际函数类型一致的指针,才能使用)
dlclose
函数int dlclose(void *handle);
- 功能:从内存中卸载动态库
- 参数:
handle
动态库访问句柄 - 返回值:成功返回0,失败返回非0(只有没有程序使用这个动态库,才能真正的被卸载)
dlerror
函数char *dlerror(void);
- 功能:获取加载、使用和卸载动态库过程中所发生的错误
- 返回值:有错误则返回指向错误信息字符串的指针,否则返回NULL
上面使用到的fprintf
函数,因为这里想使用的是错误输出,所以使用的标准错误流stderr
,与标准输出流 stdout
不同,标准错误流用于输出错误和警告信息。
动态加载的辅助工具
-
查看符号表:
nm
-
列出目标文件、可执行程序、静态库、动态库中的函数
nm libmath.a # 输出 calc.o: 0000000000000000 T add 0000000000000018 T subshow.o:U printf 0000000000000000 T show
-
-
查看依赖:
ldd
-
查看可执行文件或者共享库所依赖的共享库
ldd test# 输出linux-vdso.so.1 (0x00007fff3b0f5000)libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007f5d1da00000)/lib64/ld-linux-x86-64.so.2 (0x00007f5d1dd50000)
-
99%的人还看了
相似问题
- MeterSphere | 接口测试请求体中,int类型的入参实现动态化变量
- 利用QRCode.js生成动态二维码页面
- 动态规划:2304. 网格中的最小路径代价
- vue.js中使用三元运算符设置动态样式
- 时态图根据时间轴动态播放热力图
- 安全框架springSecurity+Jwt+Vue-1(vue环境搭建、动态路由、动态标签页)
- SwiftUI 如何动态开始和停止播放永久重复(repeatForever)动画
- OpenLayers实战,WebGL图层根据Feature要素的变量动态渲染多种颜色的三角形,适用于大量三角形渲染不同颜色
- onnx模型转换opset版本和固定动态输入尺寸
- 请问DasViewer是否支持与业务系统集成,将业务的动态的数据实时的展示到三维模型上?
猜你感兴趣
版权申明
本文"C语言的静态库和的动态库":http://eshow365.cn/6-11299-0.html 内容来自互联网,请自行判断内容的正确性。如有侵权请联系我们,立即删除!
- 上一篇: OpenCV自学笔记十三:图像梯度
- 下一篇: 西瓜书读书笔记整理(六)—— 第六章 支持向量机