文章目录
1. 静态库和动态库
2. 静态库链接
3. 动态库链接
1. 静态库和动态库
链接(linking)是编译过程的最后一步,它将多个目标文件( .o
文件)和库文件结合在一起,生成一个可执行的二进制文件。链接的主要任务是解析符号引用,合并代码和数据段,并处理重定位信息。
对于整个gcc的编译过程命令解析查看下面这篇文章:
gcc命令以及GDB调试选项-CSDN博客
静态库和动态库是根据链接时机的不同来划分的,它们主要区别在于库文件在程序运行中的加载和使用方式。
静态库
静态库在编译和链接阶段就会被包含在可执行程序里,生成的可执行文件已经内嵌了所有的库代码,具备独立运行的能力。具体特点如下:
- 内嵌到可执行文件中:静态库的代码在编译时会被复制到最终的可执行文件中,程序运行时不需要依赖外部库文件。
- 独立性强:生成的可执行文件独立,不依赖外部库,方便分发和部署。
- 性能较好:由于不需要在运行时加载库,程序启动速度较快。
- 更新复杂:如果库代码有bug或需要升级,所有使用该库的程序都需要重新编译和链接,维护工作量较大。
- 空间浪费:每个使用静态库的可执行文件中都会包含一份库的代码,如果多个程序都使用同一个静态库,会导致空间浪费。
动态库
动态库在程序运行时才被加载,多个程序可以共享一份动态库,从而节省存储空间。具体特点如下:
- 运行时加载:动态库在程序运行时根据需要加载,不会在编译时被复制到可执行文件中。
- 共享性高:多个程序可以共享同一个动态库,节省内存和存储空间。
- 更新方便:如果动态库有bug或者需要升级,只需要替换新的库文件即可,不需要重新编译和链接使用该库的所有程序。
- 启动速度慢:由于在运行时需要加载库,程序启动速度相对较慢。
- 依赖性强:可执行文件运行时依赖外部的动态库文件,如果库文件缺失或版本不兼容,程序可能无法正常运行。
接下来创建三个文件 main.c
、add.c
和 add.h
来讲解静态库链接和动态库链接的过程。
main.c
#include <stdio.h>
#include "add.h"
int main(int argc, char *argv[])
{
printf("%d\n", add(10, 10));
printf("%d\n", add(20, 20));
return 0;
}
add.c
#include "add.h"
int add(int a, int b)
{
return a + b;
}
add.h
#ifndef __ADD_H
#define __ADD_H
int add(int a, int b);
#endif
2. 静态库链接
静态库的名字一般为 “libxxx.a”。利用静态库编译生成的可执行文件比较大,因为整个函数库的所有数据都被整合进了可执行文件中。
优点:
- 不需要外部函数库支持。
- 加载速度快。
缺点:
- 静态库升级时,程序需要重新编译。
- 多个程序调用相同库,静态库会重复载入内存,造成内存浪费。
静态库的制作步骤如下:
-
编译
add.c
文件为目标文件add.o
:gcc add.c -o add.o -c
-
使用
ar
工具将目标文件add.o
打包成静态库libadd.a
:ar -rc libadd.a add.o
静态库的使用步骤如下:
1. 编译 main.c
文件,并链接静态库 libadd.a
,生成可执行文件 output
:
gcc main.c -o output -ladd -L.
-L.
表示在当前目录(.
表示当前目录)中查找库。-ladd
表示链接库名为libadd.a
(省略了前缀lib
和后缀.a
)。
2. 运行生成的可执行文件 output
:
./output
运行结果:
20
40
gcc add.c -o add.o -c
:这条命令将add.c
文件编译为目标文件add.o
。ar -rc libadd.a add.o
:这条命令将目标文件add.o
打包成静态库libadd.a
。gcc main.c -o output -ladd -L.
:这条命令将main.c
文件编译并链接静态库libadd.a
,生成可执行文件output
。-L.
选项用于指定库文件的搜索路径为当前目录。-ladd
选项用于指定链接的库名为libadd.a
。
静态库在编译期间被整合到可执行文件中,提供了加载速度快和独立性高的优点,但在库升级和内存利用方面存在一些缺点。
3. 动态库链接
动态库名字一般为 “libxxx.so”,又称共享库。动态库在编译时没有被编译进可执行文件,所以可执行文件比较小。程序运行时,操作系统帮我们找到库,并跟程序链接起来。
优点:
- 多个程序可以使用同一个动态库,节省内存。
- 库可以升级,而不需要重新编译应用程序。
缺点: 加载速度较慢,因为需要在运行时找到并加载库。
动态库的制作步骤如下:
1. 编译 lib.c
文件为动态库 libtest.so
:
gcc -shared -fPIC lib.c -o libtest.so
2. 将生成的动态库 libtest.so
复制到 /usr/lib
目录(需要超级用户权限):
sudo cp libtest.so /usr/lib/
动态库的使用步骤如下:
1. 编译 main.c
文件,并链接动态库 libtest.so
,生成可执行文件 output
:
gcc main.c -L. -ltest -o output
-L.
表示在当前目录(.
表示当前目录)中查找库。-ltest
表示链接库名为libtest.so
(省略了前缀lib
和后缀.so
)。
2. 运行生成的可执行文件 output
:
./output
运行结果:
20
40
gcc -shared -fPIC lib.c -o libtest.so
:这条命令将lib.c
文件编译为动态库libtest.so
。-shared
表示创建共享库,-fPIC
表示生成与位置无关的代码(Position Independent Code)。sudo cp libtest.so /usr/lib/
:这条命令将生成的动态库libtest.so
复制到系统的库目录/usr/lib
中。gcc main.c -L. -ltest -o output
:这条命令将main.c
文件编译并链接动态库libtest.so
,生成可执行文件output
。-L.
选项用于指定库文件的搜索路径为当前目录。-ltest
选项用于指定链接的库名为libtest.so
。
动态库在编译时没有被编译进可执行文件,而是在程序运行时由操作系统加载,从而提供了节省内存和库可升级的优点,但在运行时加载速度较慢。