库
库的存在,大大方便了我们进行编程。因为有了库,我们不必再从0开始,例如我们大多数人C语言写的第一个程序Hello World!都是用了库函数。以printf为例,我们只需要在程序源代码中包含<stdio.h>这个头文件之后,就可以使用printf函数了。这极大的方便了编程。同时库所带来的好处在于,头文件和库文件相结合的访问机制。有时候我们只想让别人使用自己实现的功能,并不想公开实现功能的源码,就可以将其制作为库文件,这样用户获取到的是二进制文件,而头文件又只包含声明部分,这样就实现了“将源码隐藏起来”的目的,且不会影响用户使用。
库文件用于程序的链接阶段,但编译器提供有 2 种实现链接的方式,分别是静态链接方式和动态链接方式,其中采用静态链接方式实现链接操作的库文件,称为静态链接库;采用动态链接方式实现链接操作的库文件,称为动态链接库。本文将介绍静态链接库。
静态链接库简介
静态链接库实现链接操作的方式很简单,即程序文件中哪里用到了库文件中的功能模块,GCC 编译器就会将该模板代码直接复制到程序文件的适当位置,最终生成可执行文件。显然,这种方式带来的优势生成的可执行文件不再需要任何静态库文件就可以独立运行;但是这个优点也会带来一些缺点,那就是生成的可执行文件体积会很大。在Linux下静态库的一般以后缀.a结尾,在Windows下一般以.lib结尾。
静态链接库创建
现在假设,我的项目拥有helloworld_c.c,helloworld_c.h两个文件。它们的内容分别如下所示。
helloworld_c.h文件
#ifndef HELLOWORLD_C_H
#define HELLOWORLD_C_H
#include <stdio.h>
#ifdef __cplusplus //使用__cplusplus宏配合extern "C"来告诉C++链接器,这是一个C接口。
extern "C"{
#endif
void Print_HelloWorld();
#ifdef __cplusplus
}
#endif
#endif //HELLOWORLD_C_H
该文件为了使得C++程序也能使用C写的库文件,使用了extern "C"的方式来包括头文件,原理详见C++调用C动态链接库
helloworld_c.c文件
#include"helloworld_c.h"
void Print_HelloWorld()
{
printf("Hello World!\n");
}
现在,我们来生成静态链接库。首先编译源文件生成相应的目标文件。
gcc -Wall -c helloworld_c.c
执行上面的命令之后,会生成一个名为helloworld_c.o的文件,现在使用 ar 压缩指令,将生成的目标文件打包成静态链接库。
ar rcs libhello.a helloworld_c.o
参数r:在库中插入模块(替换)。当插入的模块名已经在库中存在,则替换同名的模块。如果若干模块中有一个模块在库中不存在,ar显示一个错误消息,并不替换其他同名模块。默认的情况下,新的成员增加在库的结尾处,可以使用其他任选项来改变增加的位置。
参数c:创建一个库。不管库是否存在,都将创建。
参数s:创建目标文件索引,这在创建较大的库时能加快时间。(补充:如果不需要创建索引,可改成大写S参数;如果。a文件缺少索引,可以使用ranlib命令添加)
同时,需要注意,库的名字不能随便起。需要遵循规则libxxx.a
静态链接库使用
下面使用该静态链接库,在main.c文件中包含helloworld_c.h文件,即可使用Print_HelloWorld()函数。
#include"helloworld_c.h"
int main()
{
Print_HelloWorld();
return 0;
}
使用静态链接库可以有下面两种方式。
直接在GCC参数里加上-static和相应的静态链接库即可。-static表示强制 GCC 编译器使用静态链接库 即可链接libhello.a库文件,生成可执行文件a.out,执行a.out效果如下所示。 可以看到成功打印了Hello World!
GCC选项里加上-L和-l。-L(大写的 L)选项用于向 GCC 编译器指明静态链接库的存储位置; -l(小写的 L)选项用于指明所需静态链接库的名称,注意这里的名称指的是 xxx 部分,且建议将 -l 和 xxx 直接连用(即 -lxxx),中间不需有空格。
生成了可执行的a.out文件其实非常大,我们来看看它的大小。