静态库(.a)

程序在编译链接的时候把库的代码链接到可执行文件中。编译出的程序运行时不再需要静态库

生成和使用静态库

这里使用gcc编译获得.o链接文件,再用gnu归档工具中的ar指令配合-rc选项(replace and create)封装库文件,最后借助makefile简化文件目录的封装操作

使用第三方库时,还可以使用ar -tv查看库中的目录列表

  • t:列出静态库中的文件
  • v:verbose 详细信息

使用静态库:

gcc编译时使用第三方静态库必须包含以下选项:

  • -I指定头文件目录
  • -L指定库路径
  • -l指定库名(不加空格),库名要把文件名前面去掉lib,后面去掉.a才是真正的库名

否则编译器不知道头文件在哪,或者不知道链接哪个库

库搜索路径

库的搜索会按照一定的顺序

  1. 从左到右搜索-L指定的目录。
  2. 由环境变量指定的目录 (LIBRARY_PATH
  3. 由系统指定的目录
    • /usr/lib
    • /usr/local/lib

样例

测试程序(共5个文件)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/////////////add.h/////////////////
#ifndef __ADD_H__
#define __ADD_H__
int add(int a, int b);
#endif // __ADD_H__
/////////////add.c/////////////////
#include "add.h"
int add(int a, int b)
{
return a + b;
}
/////////////sub.h/////////////////
#ifndef __SUB_H__
#define __SUB_H__
int sub(int a, int b);
#endif // __SUB_H__
/////////////add.c/////////////////
#include "add.h"
int sub(int a, int b)
{
return a - b;
}
///////////main.c////////////////
#include <stdio.h>
#include "add.h"
#include "sub.h"

int main( void )
{
int a = 10;
int b = 20;
printf("add(10, 20)=%d\n", a, b, add(a, b));
a = 100;
b = 20;
printf("sub(%d,%d)=%d\n", a, b, sub(a, b));
}

然后我们编写makefile文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
lib = libmymath.a

$(lib):add.o sub.o
ar -rc $@ add.o sub.o

add.o:add.c
gcc -c add.c -o add.o

sub.o:sub.c
gcc -c sub.c -o sub.o

.PHONY:clean
clean:
rm -f *.o *.a

.PHONY:output
output:
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib

lib作为makefile中的变量,储存了库名,默认的make指令用于打包库文件,其依赖的add.osub.o会自动调用后两句编译产生。

make output指令自动准备好目录并拷贝头文件和静态库

然后编译产生可执行程序

1
gcc main.c -o mycmd -I ./lib/include -L ./lib/mymathlib -lmymath

注意,使用第三方库时-I,-L-l缺一不可

但如果安装了第三方库时,即将库文件/库文件的软链接拷贝至/lib64/,将头文件/头文件的软链接拷贝至/usr/inlude/,使gcc能够自动搜索到路径,则编译时只需用-l指定库名即可

动态库(.so)

动态库的作用是使程序在运行时才去链接动态库的代码,多个程序共享使用库的代码,从而使编译出的可执行文件更小,节省磁盘空间。使用动态库时,动态库也要加载到内存中

使用动态库底层的过程

  1. 使用动态库编译可执行程序:一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  2. 动态链接: 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制(加载)到内存中,这个过程称为动态链接(dynamic linking)
  3. 运行时调用动态库:调用来自动态库的函数时,实现函数功能的机器码将由动态库提供

生成和使用动态库

这里使用gcc-fPIC(产生位置无关码position independent code)和-c编译获得.o链接文件,再用gcc指令配合封装库文件,最后借助makefile简化文件目录的封装操作

使用动态库:

gcc编译时使用第三方静态库必须包含以下选项:

  • -I指定头文件目录
  • -L指定库路径
  • -l指定库名(不加空格),库名要把文件名前面去掉lib,后面去掉.a才是真正的库名

否则编译器不知道头文件在哪,或者不知道链接哪个库

特别的,当动静态库同时存在时,gcc默认优先使用动态库,除非加-static参数

但是,仅仅编译出可执行文件还不够,因为可执行文件还找不到动态库,所以我们还得告诉系统中的加载器,我们的动态库在哪,所以我们要把动态库安装到/lib64/,拷贝文件或建立软连接都可以(需要sudoroot权限)

亦或者修改环境变量LD_LIBRARY_PATH

示例

文件代码同上,这里展示makefile文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
lib = mymathlib.a

$(lib):add.o sub.o
gcc -shared -o libmymath.so *.o

add.o:add.c
gcc -fPIC -c add.c -o add.o

sub.o:sub.c
gcc -fPIC -c sub.c -o sub.o

.PHONY:clean
clean:
rm -f *.o *.a

.PHONY:output
output:
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.so lib/mymathlib

库搜索路径

库的搜索会按照一定的顺序

  1. 从左到右搜索-L指定的目录。
  2. 由环境变量指定的目录 (LIBRARY_PATH
  3. 由系统指定的目录
    • /usr/lib
    • /usr/local/lib

小结

库文件命名规则libxxx.a/libxxx.so

库文件名称->库名:去掉前缀lib,去掉后缀.so/.a

使用动态库的可执行程序需要安装动态库或修改环境变量LD_LIBRARY_PATH