静态库
1.mkdir staticdir
2.cd staticdir/
3.vi add.h
#ifndef _ADD_H__
#define _ADD_H__
int add(int n,int m);
#endif
vi add.c
#include "add.h"
int add(int n,int m){
return n+m;
}
vi min.h
#ifndef _MIN_H__
#define _MIN_H__
int min(int n,int m);
#endif
vi min.c
#include "min.h"
int min(int n,int m){
return n-m;
}
4.gcc -c *.c 编译不链接
生成add.o min.o
下面是静态库操作部分
ar指令:ar [选项] 静态库文件名 目标文件列表
-r - 将目标文件插入到静态库中,已存在则更新
-q - 将目标文件追加到静态库尾
-d - 从静态库中删除目标文件
-t - 列表显示静态库中的目标文件
-x - 将静态库展开为目标文件
ar -r liboper.a add.o min.o lib是必备的 oper只是个名字
打包成静态库文件:ar -r libxxx.a xxx.o …
生成liboper.a静态库
ar -t liboper.a 查看.o文件的包含
ar -d liboper.a min.o 从静态库中删除min.o
ar -q liboper.a min.o 队列式加入
ar -x liboper.a 展开.a 里面的内容,会把.o的文件都展出来,放到当前目录中
main.c
#include "add.h"
#include "min.h"
#include <stdio.h>
int main(){
int a=20,b=20;
printf("%d\n",add(a,b));//有了静态库liboper.a没有.h也运行
printf("%d\n",min(a,b));
return 0;
}
5.静态库的调用
在.c文件中使用静态库中的函数 需要包含头文件,即头文件目录
gcc -I main.c中包含的头文件的存放位置目录 main.c -l静态库名字(这里是oper)-L 静态库存放位置目录
-I 头文件目录 -l链接静态库/动态库 - L查找静态库/动态库的路径
第一个是大i,第二个是小L 即使是在当前目录,也需要配置 -I 或者 -L 后面加 .
./staticlib/ 这是.h存储的位置
gcc -I ./staticlib/ main.c -loper -L. //.是当前路径
当liboper.a提供出去时,存储目录会变,例如我把.a移动到上层,终端cd到桌面
目录就变成 .桌面/staticdir/
gcc -I .桌面/staticdir/staticlib/ main.c -loper -L ./staticdir/
也可以通过配置当前终端变量
export CPATH=$CPATH:/目录
export LIBRARY_PATH=$LIBRARY_PATH: /目录 - 链接时查找静态库/共享库的路径
两个都配置后,gcc main.c -loper 就可以直接运行,不需要在输入地址,但是这个配置只对当前终端
有效,在运行一个终端就没用了。
如果是bashrc处添加就是永久
sudo vi ~/.bashrc 永久配置
source ~/.bashrc 保存执行
pwd用于查看当前目录名
gcc -static -c array.c //表明这个文件是用来生成静态库的
动态(共享)库
cp -rf staticlib sharelib //拷贝并重命名了一份刚刚写的文件夹和内容
1.创建动态库
.c.h编辑完毕
编译所有的.c文件
gcc -c -fpic x.c
生成动态库
gcc -shared *.o -o lib动态库名.so ;//这是前面所有的.c文件都已经用-fpic编译过的写法
gcc -shared -fpic *.c -o lib动态库名.so ;//另一种
gcc -I ./sharedlib/ main.c -l动态库名 -L ./sharedlib/
这个时候动态库不能.a.out直接运行
2.使用动态库
1.编写代码,包含头文件,使用动态库中的函数
2.gcc -I 头文件路径 main.c-l动态库名 -L 动态库路径
3.链接动态库即使生成了可执行文件,该可执行文件依然不能运行
LD_LIBRARY_PATH 的作用是在执行可执行文件程序时,去指定位置链接动态库
动态库必须用这个配置:
export LD_LIBRARY_PATH=$ LD_LIBRARY_PATH:./sharedlib/
静态库和动态库的区别:
1.
生成步骤不一样
静态库是ar -r lib静态库名.a *.o
动态库是gcc -shared *.o -o lib动态库名.so
2.
静态库文件没有’x’这个权限,动态库有’x’权限(即可以直接执行,所以最后执行是跑到动态库中)
3.
链接静态库时,是把调用函数的指定用静态库中的二进制代码来替换,
链接动态库时,是直接在函数调用下嵌入函数在动态库中的相对地址
使用静态库生成的可执行程序比较大
使用动态库生成的比较小
4.
链接静态库,编译时比较慢,执行效率高
链接动态库,编译时比较快,执行效率低
5.
链接静态库,生成的执行文件在运行过程中不依赖于静态库文件
链接动态库,生成的执行文件在运行过程中依赖于动态库的LD_LIBRARY_PATH
链接动态库生成的可执行程序在运行时查找动态库的路径
6.
静态库其实本质上是对目标文件进行打包,但是动态库其实是对目标文件进行进一步的编译,
是可以执行的二进制指令
7.
如果liboper.a liboper.so都有
gcc -I main,c -loper -L.//首选链接的是动态库
那么我们如何选择连接静态库:
gcc -I -static main,c -loper -L.//强制链接静态库
加载共享动态库
loadsharedlib.c
#include <stdio.h>
#include <dlfcn.h>
typedef int (*FUNC)(int,int);
int main(){
void *handle = dlopen("liboper.so",RTLD_NOW);
if(handle == NULL){
printf("%s\n",dlerror());//获取错误信息
return -1;
}
FUNC f1,f2,f3,f4;
f1 = dlsym(handle,"add");//根据标识符获取该标识符在动态库中的相对地址
if(f1 == NULL){
printf("%s\n",dlerror());
return -1;
}
int ret = f1(1024,9527);
printf("ret = %d\n",ret);
f2 = dlsym(handle,"minus");
ret = f2(1024,9527);
printf("ret = %d\n",ret);
f3 = dlsym(handle,"div");
ret = f3(9527,1024);
printf("ret = %d\n",ret);
dlclose(handle);
return 0;
}
编译的时候必须使用-ldl
gcc loadsharedlib -ldl
辅助工具
•nm: 查看目标文件、可执行文件、静态库、共享库中的符号列表。
•ldd: 查看可执行文件和共享库的动态依赖。// 例如ldd a.out 查看a.out依赖的动态库
•ldconfig: 共享库管理。
事先将共享库的路径信息写入/etc/ld.so.conf配置文件中,ldconfig根据该配置文件生成/etc/ld.so.cache缓冲文件,并将该缓冲文件载入内存,借以提高共享库的加载效率。
系统启动时自动执行ldconfig,但若修改了共享库配置,则需要手动执行该程序。
•strip: 减肥。去除目标文件、可执行文件、静态库和共享库中的符号列表、调试信息等。
•objdump: 显示二进制模块的反汇编信息。//转为汇编代码
如果在实际开发中,有一个.c文件修改了,需要编译哪些动态库
1.c文件所在的动态库
2.链接了该动态库的所有动态库和可执行程序都需要重新编译
通过ldd可以检测,从而达到目的