完整教程:静态&动态链接库

这是我学习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老师,请继续努力吧!

posted @ 2025-11-30 15:48  yangykaifa  阅读(2)  评论(0)    收藏  举报