完整教程:静态&动态链接库
这是我学习Linux的第一天,虽然我现在还是废柴,但是我相信我会成为明日之星。
初识这两个概念,是我大二时候进组跟老师做项目,具体做啥我忘了,但是对一个没有前置知识的小白来说,我真是一脸懵,只能照葫芦画瓢的把文件放到指定文件夹,还半天放不对。
静态链接库(Static Library)和动态链接库(Dynamic Library,又称共享库)是程序开发中实现代码复用的两种核心方式,它们在链接时机、文件体积、更新机制等方面有本质区别。最核心的区别在于:
- 静态链接库:在程序编译链接阶段,库中的代码会被完整复制到最终的可执行文件中。程序运行时不依赖外部库文件,可独立执行。
- 动态链接库:在程序运行时才被加载到内存,库代码不会被复制到可执行文件中,而是由多个程序共享同一份库文件。程序运行时必须依赖外部库文件存在。
一、静态链接库
- 工作流程
- 编译阶段:将库的源代码编译为目标文件(
.o 、.obj) - 打包阶段:用工具(如
ar)将多个目标文件打包为静态库(.a 、.lib) - 链接阶段:编译器将静态库中被程序引用的代码完整复制到可执行文件中。
- 运行阶段:直接运行可执行文件。
- 编译阶段:将库的源代码编译为目标文件(
- 创建与使用(Linux环境下)
以一个简单的打印函数为例子吧。
MyPrint.h
#ifndef __MYPRINT_H
#define __MYPRINT_H
void print_HW(void);
#endif
MyPrint.c
#include <stdio.h>
#include "MyPrint.h"
void print_HW(void){
printf("Hello World!");
}
test.c
#include "MyPrint.h"
int main(){
print_HW();
return 0;
}
OK,现在开始将MyPrint打包为静态链接库吧
gcc -c MyPrint.c -o MyPrint.o
ar rcs libMyPrint_Static.a MyPrint.o
#首先我们得到目标文件xxx.o
#然后通过ar(r 替换文件,c 创建库,s 生成索引)打包为静态链接库
#其中格式为: ar 【选项】lib【name】.a 目标文件,例如此处我选项为rcs, name为MyPrint_Static 目标文件为MyPrint.o
现在我们得到了动态链接库:libMyPrint_Static.a,接下来我们进行main的编译和链接:
gcc main.c -o main_static -L. -lMyPrint_Static
#在链接过程中编译器默认回去/usr/lib or /usr/local/lib等地方去找
#因此使用-L. 指定到当前路径查找
#-l[name] 就是你之前命明的名字了
至此我们终于得到了可执行文件了
./main_static
结果:
那我现在要修改代码,例如在Hello World! 后加个回车,我需不需要重新编译main.c呢?还是说直接编译生产MyPrint的静态库?
答案当然是都要了,因为我们前面提到了对于静态库,库中的代码会被完整复制到最终的可执行文件中,现在我们修改了MyPrint.c,是需要重新生产可执行文件的,这将在动态库的描述中形成对比。我就不演示辣,真懒。。。
二、动态链接库
接下来,还是套话起手吧…
- 工作流程:
- 编译阶段:生成位置无关代码(PIC,Position-Independent Code) 的目标文件(确保库可被加载到内存任意位置)。
- 打包阶段:用编译器将目标文件打包为动态库(
.so或.dll)。 - 编译器仅在可执行文件中记录动态库的引用信息(如库名、函数地址偏移),不复制库代码。
- 程序启动时,操作系统的动态链接器(如 Linux 的
ld.so,Windows 的 ntdll.dll)会将动态库加载到内存。
多个程序可共享同一份动态库的内存副本(节省内存)。
- 创建和使用
同样我们使用上面的打印代码。首先得到位置无关的目标文件
gcc -c -fPIC MyPrint.c -o MyPrint.o
#此处的-fPIC意味着代码将被编译为位置无关的目标文件
随后转为动态链接库
gcc -shared MyPrint.o -o libMyPrint_Dynamic.so
#-shared选项将会把目标文件转为以.so为后缀的动态链接库哦
然后,编译链接
gcc -main.c -o main_dynamic -L. -lMyPrint_Dynamic
try it!
哦不,我们找不到动态链接库,或许可以把生成的动态链接库放到/usr/lib下,或者更常用的方法,设置一个变量指定编译器查找动态库的路径。
export LD_LIBRARY_PATH="your path"
ok,现在我们成功了
接下来,前面没讨论完的修改问题,我将MyPrint.c打印内容修改为“Hello Friends!\r”,然后重新生产动态库。
gcc -c -fPIC MyPrint.c -o MyPrint.o
gcc -shared MyPrint.o -o libMyPrint_Dynamic.so
./main_dynamic
直接运行:
有没有发现,我并没有重新生成可执行文件,这就是运行时加载的魅力,当任务庞大且复杂,我想修改一个小模块,只需要重新编译这个小模块,而不是把整个项目编译一遍。
三、总结
| 特性 | 静态链接库(.a/.lib) | 动态链接库(.so/.dll) |
|---|---|---|
| 可执行文件体积 | 较大(包含库代码副本) | 较小(仅包含引用信息) |
| 内存占用 | 较高(多个程序运行时会加载多份库代码副本) | 较低(多个程序共享同一份库内存副本) |
| 运行依赖性 | 无(可独立运行,不依赖外部文件) | 有(必须存在对应动态库,否则无法运行) |
| 更新维护 | 麻烦(库更新后,所有依赖程序需重新编译链接) | 方便(库更新后,无需重新编译程序,直接替换库即可) |
| 编译链接速度 | 较快(链接时直接复制代码) | 较慢(链接时需处理动态引用,运行时需加载库) |
| 移植性 | 较好(可执行文件独立,无需携带库) | 较差(移植时需同时携带动态库) |
最后,感谢观看,感谢AI老师,请继续努力吧!

浙公网安备 33010602011771号