在C、C++中我们使用过标准库,比如在使用strerror
、vector
、string
等时,都只是调用了这些函数接口,这些都是需要具体的实现。
让我们来看看C语言库:
将来运行程序,需要二进制文件和库文件
看下C++标准库:
在Linux系统中,.so
结尾是动态库,以.a
结尾是静态库;
在Windows中,.dll
结尾是动态库,lib
结尾是静态库。
静态库(.a
):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库。
.c
文件可形成一个.o
文件,将这些.o
文件链接形成可执行文件,头文件是一个手册,提供函数声明,告诉用户如何使用,.o
文件提供实现,我们只需要补充一个main
文件,调用头文件提供的方法,然后和.o
文件进行连接,就能形成可执行文件。
简单制作一个静态库
// add.h
#pragma once
int add(int x, int y);
// add.c
#include "add.h"
int add(int x, int y)
{
return x + y;
}
// sub.h
#pragma once
int 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 "
-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 once
int add(int x, int y);
// add.c
#include "add.h"
int add(int x, int y)
{
return x + y;
}
// sub.h
#pragma once
int 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
指令,将所有的配置文件重现加载一下,然后程序就能够正常运行。
此时程序就可以正常运行:
动静态库同时存在时,默认连接的是动态库:
此时对应的可执行程序的体积很小:
那么如何使用静态库?
只需在后面加一个-static
此时对应的可执行文件体积很大:
如果你没有使用-static
并且只提供.a
,只能静态库连接当前的.a
库,其他库正常动态连接。
-static
的意义是什么呢?
必须强制的将程序进行静态连接,这就要求连接的任何库都必须提供对应的静态库版本。