目录
函数库
函数库是一系列事先被编写好的函数的集合,可以被其他用户所复用。
函数库分别静态库与动态库两种。
静态库
静态库是一系列目标文件(.o文件)的集合,在程序的链接过程中,被调用的静态库与其他目标文件一起,被合并成一个可执行文件。由于链接时已将库中的所有函数合并到可执行文件中,因此在执行程序时不再需要静态库文件。
在Windows系统下,静态库文件的后缀名为.lib,在linux下,静态库文件的后缀名为.a。
动态库
与静态库不同,动态库是在程序执行时才载入内存的。在编译时会进行简单的引用,但不会将库中的函数链接到可执行完文件中。因此,使用动态库得到的可执行文件较小。
动态库在Windows下后缀名是.dll,在Linux下是.so。
正因为动态库是在程序执行时被调用,因此在程序执行时,要保证特定路径(以linux为例,默认是/usr/lib)下存在该动态库文件,否则程序会运行出错(“找不到文件:QT5Core.dll”)。
使用gcc制作静态库与动态库
1.准备环境
- 虚拟机:Ubuntu18.0.2
- 使用软件:vim,gcc
- 代码:hello.h,hello.c,main.c
hello.h (包含main要使用的函数声明)
#ifndef __HELLO_H__
#define __HELLO_H__
void hello(const char* str);//当函数不需要对地址传递参数进行修改时,最好加上const
#endif
hello.c (包含对应的函数定义)
#include "stdio.h"
void hello(const char* str){
printf("Hello %s^_^!",str);
}
main.c (主函数)
#include "stdio.h"
#include "hello.h"
int main(){
hello("Everyone!");
return 0;
}
2.生成目标文件
在当前目录下输入指令:
gcc -c hello.c
生成需要的目标文件hello.o。
3.通过目标文件制作静态库.a文件
静态库文件名的命名规范是以 lib 为前缀,紧接着跟静态库名,扩展名为.a。
创建静态库用 ar 命令。在系统提示符下键入以下命令将创建静态库文件libmyhello.a。
ar -cvr libmyhello.a hello.o
4.使用静态库文件制作可执行文件
使用以下指令:
gcc -o run main.c -L. –lmyhello
#-L.表示要链接的库在当前目录下
或者
gcc main.c libmyhello.a -o run
即可生成可执行文件。
5.通过目标文件制作动态库.so文件
使用以下指令进行制作:
gcc -shared -fPIC -o libmyhello.so hello.o
#-shared 该选项指定生成动态连接库(让连接器生成 T 类型的导出符号表,有时候也生成 弱连接 W 类
#型的导出符号),不用该标志外部程序无法连接。相当于一个可执行文件。
#-fPIC 表示编译为位置独立的代码,不用此选项的话编译后的代码是位置相关的所以动态载 入时是通过代
#码拷贝的方式来满足不同进程的需要,而不能达到真正代码段共享的目的。
6.通过动态库制作可执行文件
gcc -o hello main.c -L. -lmyhello
#or
gcc main.c libmyhello.so -o hello
此时,直接执行生成的hello文件可能会出错,如图所示:
这是因为运行时,程序是到 /usr/lib 中找库文件的,不是在当前目录找。因此,将libmyhello.so复制到/usr/lib即可。
此时再运行可执行文件,就能得到和静态库文件一样的结果了:
静态库与动态库的优先问题
假设下面这种情况:现在马上要制作可执行文件,当前目录下既有静态库又有动态库,而且这两个库同名(都为libmyhello)。那么在使用“gcc -o hello main.c -L. -lmyhello”制作可执行文件时,使用的是静态库还是动态库呢?
将当前目录下除了头文件与源代码的其他文件全部删除,并重新制作一份hello.c的静态库文件与动态库文件,二者名称都为libmyhello,只是后缀不同。
删除/usr/lib下的动态库。制作完成后,执行时若提示找不到库文件,说明链接时使用了动态库文件(因此才没有使用静态库的函数),否则使用的静态库进行链接。
由图可见,当存在同名静态库与动态库时,会优先与动态库文件进行链接。
实例:分别使用两种库制作包含多个源文件的可执行文件
写一个x2x函数以及一个x2y函数(功能自定),main函数代码将调用x2x和x2y ;将这3个函数分别写成单独的3个 .c文件,并用gcc分别编译为3个.o 目标文件;将x2x、x2y目标文件分别生成1个 .a 静态库文件以及一个.so动态库文件, 再各自生成最终的可执行程序,记录文件的大小。
main.c
#include "stdio.h"
#include "testHead.h"
int main(){
printf("x2x: %f \n",x2x(2.0,5.0));//返回二数的商
printf("x2y: %f \n",x2y(2,5));//返回二数的积
return 0;
}
testHead.h
#ifndef __TESTHEAD_H_
#define __TESTHEAD_H_
float x2x(float x,float y);
int x2y(int x,int y);
#endif
x2x.c
float x2x(float x,float y){
return x/y;
}
x2y.c
int x2y(int x,int y){
return x*y;
}
制作静态库并链接执行
制作动态库并执行
与前文介绍方法一致,制作了动态库后,将其复制到/usr/lib下,即可正常执行。
注意这里静态库生成的可执行文件要与动态库可执行文件在名称上区分开。
对比
从输出结果上看,二者完全一致,无任何差别。
从大小上看,静态库比动态库要小得多,仅为动态库文件的1/3。而生成的可执行文件相差不大。