动静态库
库
库(Library)说白了就是一段编译好的二进制代码(.o二进制文件),加上头文件就可以供别人使用。
什么时候我们会用到库呢?一种情况是某些代码需要给别人使用,但是我们不希望别人看到源码,就需要以库的形式进行封装,只暴露出头文件。另外一种情况是,对于某些不会进行大的改动的代码,我们想减少编译的时间,就可以把它打包成库,因为库是已经编译好的二进制了,编译的 时候只需要 Link 一下,不会浪费编译时间。
上面提到库在使用的时候需要 Link,Link 的方式有两种,静态链接和动态链接,于是便产生了静态库和动态库。
一、静态库
1.1 静态库概念
静态库即静态链接库(Windows 下的 .lib,Linux 和 Mac 下的 .a)。之所以叫做静态,是因为静态库在编译的时候会被直接拷贝一份,复制到目标程序里,这段代码在目标程序里 就不会再改变了。静态库的好处很明显,编译完成之后,库文件实际上就没有作用了。目标程序没有外部依赖,直接就可以运行。当然其缺点也很明显,就是会使用目标程序的体积增大。
1.2 静态库的制作
mymath.h文件
#pragma once
#include<stdio.h>
#include<assert.h>
extern int AddtoVal(int strat, int end);
mymath.c文件
#include"mymath.h"
int AddtoVal(int strat, int end)
{
assert(end >= strat);
int result = 0;
for(int i = strat; i <= end; ++i)
{
result += i;
}
return result;
}
myprint.h文件
#pragma once
#include<stdio.h>
#include<time.h>
extern void Print(const char* msg);
myprint.c文件
#include"myprint.h"
void Print(const cahr *msg)
{
printf("%s, %lld",msg, (long long)time(NULL));
}
Makefile文件(重点)
libmylib.a:mymath_s.o myprint_s.o
ar -rc libmylib.a mymath_s.o myprint_s.o
mymath_s.o:mymath.c
gcc -c mymath.c -o mymath_s.o -std=c99
myprint_s.o:myprint.c
gcc -c myprint.c -o myprint_s.o -std=c99
.PHONY:S_lib
S_lib:
mkdir -p lib-static/lib
mkdir -p lib-static/include
cp *.a lib-static/lib
cp *.h lib-static/include
.PHONY:clean
clean:
rm -rf *.a *.o lib-static
解读:
ar是gnu归档工具,它就是用来把*.o文件打包为库文件( *.a 文件),-rc(rc为replace and create)
我们在使用C语言的库函数时,除了要用到相应的库文件,还用包含库函数的头文件,所以我们要把库文件和头文件指定的放在相应的目录当中。如上面的cp *.a lib-static/lib和*cp .h lib-static/include。
总结:
静态库的制作就是把函数的声明(*.h)与定义 ( *.c 或者 *.cpp)分离,然后把 *.c或 *.cpp编译为 *.o文件,紧接着把 *.o 打包为 *.a文件,最后分别把 *.a文件 与 *.h文件指定放在相应的目录当中即可。
1.3 静态库的使用
-
将自己的头文件和库文件拷贝到系统的头文件路径(/usr/include/)和库文件路径(/usr/lib64/)下!!!(相当于安装库),然后指定要链接的库文件
sudo cp ./*.h /usr/include/ sudo cp ./*.a /usr/lib64/ gcc mytest.c -o mytest -l mymath //gcc mytest.c -o mytest -lmymath 也可以
其中的-l选项就是指定要链接的库文件,后面跟着的是库文件名。(去掉前缀lib和后缀.a就是库文件名)
(不推荐)
-
在编译的时候告诉编译器头文件和库文件的查找路径,并且指定要链接的库文件
gcc mytest.c -o mytest -I ./lib-static/include/ -L ./lib-static/lib/ -l mylib //gcc mytest.c -o mytest -I./lib-static/include/ -L./lib-static/lib/ -lmylib 也可以
二、动态库
2.1 动态库的概念
动态库
动态库即动态链接库(Windows 下的 .dll,Linux 下的 .so,Mac 下的 .dylib)。与静态库相反,动态库在编译时并不会被拷贝到目标程序中,目标程序中只会存储指向动态库的引用。等到程序运行时,动态库才会被真正加载进来。动态库的优点是,不需要拷贝到目标程序中,不会影响目标程序的体积,而且同一份库可以被多个程序使用(因为这个原因,动态库也被称作共享库)。同时,编译时才载入的特性,也可以让我们随时对库进行替换,而不需要重新编译代码。动态库带来的问题主要是,动态载入会带来一部分性能损失,使用动态库也会使得程序依赖于外部环境。如果环境缺少动态库或者库的版本不正确,就会导致程序无法运行(Linux 下喜闻乐见的 lib not found 错误)。
2.2 动态库的制作
mymath.h文件
#pragma once
#include<stdio.h>
#include<assert.h>
extern int AddtoVal(int strat, int end);
mymath.c文件
#include"mymath.h"
int AddtoVal(int strat, int end)
{
assert(end >= strat);
int result = 0;
for(int i = strat; i <= end; ++i)
{
result += i;
}
return result;
}
myprint.h文件
#pragma once
#include<stdio.h>
#include<time.h>
extern void Print(const char* msg);
myprint.c文件
#include"myprint.h"
void Print(const cahr *msg)
{
printf("%s, %lld",msg, (long long)time(NULL));
}
Makefile文件
libmylib.so:mymath_d.o myprint_d.o
gcc shared mymath_d.o myprint_d.o -o libmylib.so
mymath_s.o:mymath.c
gcc -fPIC -c mymath.c -o mymath_d.o
myprint_d.o:myprint.c
gcc -fPIC -c myprint.c -o myprint_d.o
.PHONY:D_lib
D_lib:
mikdir -p lib-dynamic/lib
mikdir -p lib-dynamic/include
cp ./*.so ./lib-dynamic/lib
cp ./*.h ./lib-dynamic/include
.PHONY:clean
rm -rf *.o *.so lib-dynamic
解读:
shared: 表示生成共享库格式
fPIC:产生位置无关码(position independent code)
我们在使用C语言的库函数时,除了要用到相应的库文件,还用包含库函数的头文件,所以我们要把库文件和头文件指定的放在相应的目录当中。如上面的cp *.so lib-dynamic/lib和*cp .h lib-dynamic/include。
总结:
**动态库的制作就是把函数的声明(*.h)与定义 ( *.c 或者 *.cpp)分离,然后把 *.c或 *.cpp编译为 *.o文件(需要使用-fPIC),紧接着把 *.o 打包为 *.a文件(使用的是gcc -shared),最后分别把 .a文件 与 .o文件指定放在相应的目录当中即可。
2.3 动态库的使用
-
将自己的头文件和库文件拷贝到系统的头文件路径(/usr/include/)和库文件路径(/usr/lib64/)下!!!(相当于安装库),然后指定要链接的库文件
sudo cp ./*.h /usr/include/ sudo cp ./*.so /usr/lib64/ gcc mytest.c -o mytest -l mymath //gcc mytest.c -o mytest -lmymath 也可以
其中的-l选项就是指定要链接的库文件,后面跟着的是库文件名。(去掉前缀lib和后缀.so就是库文件名)
(不推荐)
-
在编译的时候告诉编译器头文件和库文件的查找路径,并且指定要链接的库文件
gcc mytest.c -o mytest -I ./lib-dynamic/include/ -L ./lib-dynamic/lib/ -l mylib //gcc mytest.c -o mytest -I./lib-dynamic/include/ -L./lib-dynamic/lib/ -lmylib 也可以
报错的大概意思是说mytest可执行文件在执行的时候找不到它所依赖的动态库,又有人说不是在编译的时候指明了动态库的路径了吗???怎么会找不到呢???因为在编译的时候,他只是告诉了gcc动态库的路径,运行的时候并没有告诉进程动态库的路径,所以当进程需要使用动态库的代码时,会由于找不到动态库而崩掉。
那么又有人会有问题,为什么静态库不会存在进程找不到静态库呢???因为它在形成可执行程序后,已经把它所需要的代码加载到了可执行程序中,它在执行的时候不会依赖我们写的库。
2.3.1 那么如何让进程知道动态库的路径呢???
-
将动态库拷贝到系统路径 **/usr/lib64/**下(不推荐)
-
通过导入环境变量的方式 — LD_LIBRARY_PATH
程序在运行的时候通过环境变量来查找自己所需要的动态库路径
把动态库的绝对路径添加到环境变量LD_LIBRARY_PATH中,在执行mytest就可以运行了。但是这个方法有一个问题,就是在下次登陆的时候,刚刚添加的环境变量就没有了。还得重新导入环境变量。
- 通过配置文件 /etc/ld.so.conf.d/
在进程运行的时候,它不仅会去系统的库路径下去找自己需要依赖的动态库文件,还会去 /etc/ld.so.conf.d/ 路径下依次扫描该路径下的文件,通过这些文件里的信息(一般为绝对路径)找到依赖动态库文件。
sudo touch /etc/ld.so.conf.d/my-104.conf
然后在 my-104.conf中写入依赖动态库的绝对路径
我们发现添加了配置文件以后,进程还是找不到自己需要的动态库,这是因为配置文件还没有生效,需要通过 ldconfig指令:让配置文件生效。
这样我们的进程就可以运行了
-
在系统路径下建立软连接
sudo ln -s /home/lk/Linux/动静态库/uslib/lib-dynamic/lib/libmylib.so /usr/lib64/libmylib.so
unlink取消软连接:
三、动静态库的对比(优缺点)
1、静态库
优点:
①静态库被打包到应用程序中加载速度快
②发布程序无需提供静态库,移植方便
缺点:
①相同的库文件数据可能在内存中被加载多份,消耗系统资源,浪费内存
②库文件更新需要重新编译项目文件,生成新的可执行程序,浪费时间。
2、动态库
优点:
①可实现不同进程间的资源共享
②动态库升级简单,只需要替换库文件,无需重新编译应用程序
③可以控制何时加载动态库,不调用库函数动态库不会被加载
缺点:
①加载速度比静态库慢
②发布程序需要提供依赖的动态库