文章目录
- 前言
- 静态库
- 静态库制作
- 静态库的生成
- 发布静态库
- 使用静态库
- 安装静态库
- 卸载静态库
- 动态库
- 动态库的制作
- 动态库的生成
- 动态库的发布
- 使用动态库
- 动态库VS静态库
前言
在C、C++中我们使用过标准库,比如在使用strerror
、vector
、string
等时,都只是调用了这些函数接口,这些都是需要具体的实现。
让我们来看看C语言库:
将来运行程序,需要二进制文件和库文件
看下C++标准库:
在Linux系统中,.so
结尾是动态库,以.a
结尾是静态库;
在Windows中,.dll
结尾是动态库,lib
结尾是静态库。
静态库
静态库(.a
):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
.c
文件可形成一个.o
文件,将这些.o
文件链接形成可执行文件,头文件是一个手册,提供函数声明,告诉用户如何使用,.o
文件提供实现,我们只需要补充一个main
文件,调用头文件提供的方法,然后和.o
文件进行连接,就能形成可执行文件。
静态库制作
简单制作一个静态库
// add.h
#pragma onceint add(int x, int y);
// add.c
#include "add.h"int add(int x, int y)
{return x + y;
}
// sub.h
#pragma onceint sub(int x, int y);
// sub.c
#include "sub.h"int sub(int x, int y)
{return x - y;
}
静态库的生成
静态库生成指令:ar -rc lib静态库名.a 需要形成静态库的文件
,ar
是gnu归档工具,rc
表示replace and create
静态库的形成本质上是将所有的.o
文件打包,因此需要先生成.o
文件
发布静态库
发布静态库就是自己的lib
拷贝给比人
例如上图是我自己制作的一个简单静态库,只需要将mylib
拷贝给别人即可。
使用静态库
将自己的mylib
拷贝到一个test
文件夹中,然后写一个main.c
文件,用于测试静态库的使用
mian.c:
#include "add.h"
#include "sub.h"
#include <stdio.h> int main() { printf("1 + 2 = %d\n", add(1, 2)); printf("1 - 2 = %d\n", sub(1, 2)); return 0; }
编译:
这里报错了,说找不到对应的头文件
头文件一般有以下两种方式来包含头文件:
- 使用
<>
来包含头文件,表示到系统指定目录下去查找头文件 - 使用
" "
来包含头文件,这种方式一般用于包含自己所写的头文件中,表示在当前源文件的统计目录下查找头文件,找到了就用,没找到再去系统指定目录下进行查找,所以对于库提供的头文件我们也可以使用" "
进行包含。
在main.c
文件中,就是使用" "
来包含我所写的头文件,但是还是会报错,理由:使用" "
所包含的头文件,会告诉编译器在main.c
同级目录下(即test
目录下)查找对应的头文件,但是add.h
、sub.h
文件在test
文件中的mylib
文件中,因此无法找到。
解决上述有三种方式:
- 将头文件直接拷贝到当前目录下
- 在代码中头文件的路劲补全,如:
#include " /mylib/include/add.h "
- 在执行 gcc 指令编译的时候加上
-I
选项,指定编译器搜索头文件的路径
系统默认的指定路劲:/usr/include
使用方法3:gcc main.c -I ./mylib/include
此时依然没有编译成功,此时不是找不到头文件,而是链接错误。gcc
在编译的时候,只会去默认路径下查找打包的头文件,不会去/mylib/include
中查找,编译器在gcc
是就找不到我的酷libmyc.a
,也就是编译链接失败。
此时可以形成main.o
文件:
解决此错误有两种方法:
- 将我们的库拷贝到系统的指定路径下,并不能完全解决,还需要指定库的名称
- 在使用
gcc
的时候添加对应的选项
-L
指定库路径-l
指定库名
为什么在搜索头文件的时候仅需指定路径呢?当你编译程序时,编译器会首先在这些默认路径下搜索所需的头文件。
在代码中已经写了头文件的具体名称,所以仅需指定头文件的路径即可。而一个路径下可以有多个库,如果只指定路劲,编译器还是不知道该去链接哪个库,因此还要在后面使用 -l 选项指定待链接的库的具体名称。
注意:去掉前缀 lib 和 后缀 .a 才是一个库的名称,建议 -l 后面紧跟库的名称。一般在使用第三方库的时候,可能不需要带 -I 或者 -L,但是 -l 指定库的名称是一定需要到,因为 gcc 默认只能找到系统调用和语言层面的库。
安装静态库
静态库的安装本质上就拷贝到系统的特定目录下。
卸载静态库
卸载静态库本质是将.h
文件和自己的静态库从默认的路劲中删除,此时就无法通过静态库来运行程序。
动态库
动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
动态库的制作
简单制作一个动态库
// add.h
#pragma onceint add(int x, int y);
// add.c
#include "add.h"int add(int x, int y)
{return x + y;
}
// sub.h
#pragma onceint sub(int x, int y);
// sub.c
#include "sub.h"int sub(int x, int y)
{return x - y;
}
动态库的生成
fPIC
:产生位置无关码(position independent code)shared
: 表示生成共享库格式- 库名规则:
libxxx.so
libmyc.so
就是生成的动态库
动态库的发布
将动态库拷贝到刚刚我们静态库的位置
使用动态库
mian.c:
#include "add.h"
#include "sub.h"
#include <stdio.h> int main() { printf("1 + 2 = %d\n", add(1, 2)); printf("1 - 2 = %d\n", sub(1, 2)); return 0; }
按照静态库的使用方法,来使用静态库:
虽然生成了可执行文件,但是可执行文件出错了
使用ldd a.out
时,发现libmyc.so => not found
,动态库没有被找到,编译期间已经告诉系统对应的头文件以及库的位置,但是这是告诉编译器,没有告诉操作系统,因此编译通过,但是无法运行。
动态库要在程序运行的时候要找到动态库加载运行。静态库为什么没有这个问题?因为静态库在编译期间已经将库中的代码拷贝到可执行程序内部了,加载和库就没有关系了。
解决该问题,有以下四种方法:
- 将库文件拷贝到系统默认的库路(
/lib64
、/usr/lib64
),不推荐使用这种方法,因为修改了系统规定的库,降低了系统的健康指数 - 在系统默认的库路径(
/lib64
、/usr/lib64
)下建立软链接
- 将自己库所在的路径,添加到系统的环境变量
LD_LIBRARY_PATH
中,该环境变量就是专门用来搜索动态库的
但是重新启动系统后,就找不到该环境变量,如果想让系统启动时自动添加该路径到LD_LIBRARY_PATH
环境变量中,可以通过修改~/.bash_profile
中的配置去实现,但是不推荐这么写,不建议修改环境变量。 - 如果想让我们的库和系统、语言自带的库一样,在程序运行的时候可以自动被找到,那我们可以
/etc/ld.so.conf.d
路径下添加一个.conf
结尾的配置文件
该配置文件里面的内容就是我们自己动态库所在的路径。添加完后执行ldconfig
指令,将所有的配置文件重现加载一下,然后程序就能够正常运行。
此时程序就可以正常运行:
动态库VS静态库
动静态库同时存在时,默认连接的是动态库:
此时对应的可执行程序的体积很小:
那么如何使用静态库?
只需在后面加一个-static
此时对应的可执行文件体积很大:
如果你没有使用-static
并且只提供.a
,只能静态库连接当前的.a
库,其他库正常动态连接。
-static
的意义是什么呢?
必须强制的将程序进行静态连接,这就要求连接的任何库都必须提供对应的静态库版本。