跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客我的书GitHub 精选镜像AI 生图工具UI配色美学关于
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C

Linux 下静态库与动态库的打包及使用

在 Linux 下可以用 ar 命令将目标文件打包成静态库(.a),用 gcc -shared -fPIC 生成动态库(.so)。编译时通过 -I、-L、-l 指定头文件路径、库路径和库名。静态库链接时代码复制到可执行文件,部署简单但占用空间大;动态库运行时加载,多进程共享,节省内存但需确保运行环境能找到 .so。解决动态库找不到的问题可以通过复制到系统路径、设置 LD_LIBRARY_PATH、或编辑 ld.so.conf.d 等方式。

不羁发布于 2026/6/18更新于 2026/8/2321 浏览
Linux 下静态库与动态库的打包及使用

从源文件到可执行程序,大致会经过预处理、编译、汇编、链接四个阶段。

文章配图

如果有一组源文件需要反复在多个项目里使用,可以把它们的目标文件(.o)打包成一个库,这样之后链接的时候直接拿这个库就行,不用每次都重新编译那些 .c。所谓的库,本质就是一堆 .o 文件的集合,相当于可执行程序的'半成品'。

文章配图

要理解库的基本操作,先来看一个简单的例子。main.c 里自己写了一个加法函数,但一般我们更关心 printf 这种 C 标准库提供的功能。用 ldd 看一下生成的可执行文件,能看到它依赖了 libc.so.6,这就是动态版的 C 标准库。

// main.c
#include <stdio.h>
#include <string.h>

int MyAdd(int x, int y) {
    return x + y;
}

int MySub(int x, int y) {
    return x - y;
}

int main() {
    int a = 10;
    int b = 20;
    printf("%d + %d = %d\n", a, b, MyAdd(a, b));
    return 0;
}

文章配图

文章配图

在 Linux 下,以 .so 结尾的是动态库,以 .a 结尾的是静态库;Windows 里则是 .dll 和 .lib。去掉前缀 lib、后缀 .so/.a 以及可能跟着的版本号,剩下的就是库的名字,比如 libc.so.6 的名字是 c。

静态库

演示用三个文件:mymath.h、mymath.c、main.c。mymath.h 声明两个函数,mymath.c 提供实现。

// mymath.h
#pragma once
int MyAdd(int, int);
int MySub(int, int);
// mymath.c
int MyAdd(int x, int y) {
    return x + y;
}

int MySub(int x, int y) {
    return x - y;
}
// main.c
#include <stdio.h>
#include <mymath.h>

int main() {
    int a = 10;
    int b = 20;
    printf("%d + %d = %d\n", a, b, MyAdd(a, b));
    return 0;
}

打包

先编译出 mymath.o,然后把 .o 用 ar 归档成静态库 libmy.a。-c 表示创建归档,-r 表示如果库内已有同名文件就替换。

文章配图

文章配图

用 ar -tv 可以看到库里面有哪些目标文件。

文章配图

文章配图

为了方便分发,通常会把头文件放到一个 include 目录,库文件放到 lib 目录,整体打包成 mylib 目录。

文章配图

使用

编译 main.c 时需要带上三个选项:-I 指定头文件路径,-L 指定库文件路径,-l 指定库名字(去掉 lib 前缀和 .a 后缀)。

gcc main.c -I ./mylib/include/ -L ./mylib/lib/ -lmy

文章配图

文章配图

文章配图

另一种偷懒的做法是把头文件和库文件直接丢到系统路径下,比如 /usr/include 和 /lib64。不过这种方式可能污染系统,通常不建议。如果非要这么干,即使文件在系统路径里了,链接时依然要手动指定 -lmy,因为 gcc 默认不会链接第三方的静态库。

sudo cp mylib/include/*.h /usr/include
sudo cp mylib/lib/*.a /lib64

文章配图

文章配图

动态库

动态库的打包方式和静态库有点区别,但文件组织方式类似。我们继续用 mymath.h、mymath.c、main.c 做演示。

打包

第一步,编译源文件时要加 -fPIC 选项,产生位置无关代码。这样生成的 .so 才能被多个进程在内存中共享。如果不用 -fPIC,加载时会需要对代码段做重定位,多个进程就会各自维护一份不同的代码副本,失去了共享的意义。

gcc -c -fPIC mymath.c -o mymath.o

文章配图

第二步,用 gcc -shared 将目标文件打包成动态库 libmyc.so。

gcc -shared mymath.o -o libmyc.so

文章配图

最后把头文件和 .so 组织到 mylib 目录下,和静态库的目录结构一样。

文章配图

使用

编译阶段和静态库完全一样,-I、-L、-l 照用。

gcc main.c -I mylib/include/ -L mylib/lib/ -lmyc

文章配图

但是,编译成功了,一运行可能报错,提示找不到 libmyc.so。这是因为编译时指定的路径只是告诉链接器上哪儿找符号,运行的时候动态链接器要从别的地方找 .so 文件。用 ldd 可以看到可执行文件缺了什么库。

文章配图

解决这个问题的办法有好几种,按场景选就行:

  1. 把 .so 复制到系统默认的共享库路径,比如 /lib/x86_64-linux-gnu/。
sudo cp mylib/lib/libmyc.so /lib/x86_64-linux-gnu/

文章配图

文章配图

  1. 在系统共享库路径下建一个软链接。
sudo ln -s /path/to/libmyc.so /lib/x86_64-linux-gnu/libmyc.so

文章配图

  1. 临时设置环境变量 LD_LIBRARY_PATH,告诉动态链接器上哪里找。
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/lib
dir

文章配图

  1. 永久生效的话,可以把 export 那行写进 ~/.bashrc。
echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/libdir' >> ~/.bashrc
source ~/.bashrc

文章配图

  1. 更规范的做法是在 /etc/ld.so.conf.d/ 下新建一个 .conf 文件,写上库所在的路径,然后执行 ldconfig 刷新缓存。
sudo sh -c 'echo "/path/to/libdir" > /etc/ld.so.conf.d/mylib.conf'
sudo ldconfig

文章配图

静态库和动态库的取舍

使用静态库,链接的时候会把库里的代码直接拷贝到可执行文件里,所以生成的文件体积大,但好处是运行时不再依赖外部的库文件,部署简单。多个程序同时运行、且都用了同一个静态库,内存里会有重复的代码。

动态库则是运行时才加载,多个进程可以共享同一份物理内存中的库,节省磁盘和内存。代价是部署时要保证目标机器上有对应的 .so 文件,否则程序跑不起来。

文章配图

动态库加载的背后

动态库(共享库)在磁盘上就是一个 .so 文件。操作系统会管理所有已经加载的库,原理还是先描述、再组织。

程序启动时,动态链接器先检查依赖,然后去预设的搜索路径里找对应的 .so。如果是第一次被加载,就映射到进程地址空间的共享区;如果其他进程已经加载过同一份库,会直接复用,只需修改新进程的映射关系。

文章配图

可执行程序在编译后就已经有了虚拟地址,代码里的符号会被编址。编址分为绝对编址和相对编址,动态库一般用相对编址,也就是位置无关代码(PIC)。最终运行的时候,通过'库在地址空间的起始地址 + 偏移量'来访问库里的函数或数据。

整个过程需要 CPU、编译器和操作系统三方协作:编译器生成位置无关的代码和段信息,操作系统负责分配地址空间、重定位和页表管理,CPU 利用 MMU 完成虚拟地址到物理地址的转换。

文章配图

库被加载之后,进程只需要在地址空间的共享区看到它的映射,调用库函数时,就是拿着这个区域的基地址加上函数在库内的偏移量去找到具体的代码,这一点和调用自己代码没太大区别。

文章配图

目录

  1. 静态库
  2. 打包
  3. 使用
  4. 动态库
  5. 打包
  6. 使用
  7. 静态库和动态库的取舍
  8. 动态库加载的背后
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Whisper 模型语音识别入门与实战指南
  • Hive 0.7.1 基于 Ubuntu 的伪分布式环境搭建与配置
  • 无需公网 IP 安全访问本地 AI 服务的几种方案
  • Midjourney 进阶:色调详解(上)
  • GitHub 下载速度提升方案汇总
  • WebLogic 简介与登录方法指南
  • Adobe Photoshop 25.0 Beta 更新详解:创意填充与神经网络滤镜
  • Python 基础语法完全指南:变量、类型与运算符
  • Java 核心面试题与答案整理(基础、并发、JVM 等)
  • PPO-VLA:强化学习如何提升机器人泛化能力
  • Spring IoC 容器与依赖注入核心机制详解
  • Xilinx PCIe IP 核详解、FPGA 实现及仿真流程(Virtex-7 Gen3)
  • Cursor + Figma MCP:AI 自动还原设计稿生成代码
  • 15. Web可访问性最佳实践:让每个用户都能平等访问
  • 实测 ToClaw 信息检索与分析能力:AI 实现先找再写
  • 2026 年 AI 论文写作工具盘点与全流程指南
  • Python 自动化办公:使用 xlutils 修改 Excel
  • Python 非官方 Google 搜索 API 使用指南
  • MySQL 数据误删恢复方案
  • 本地 Docker 部署 Appsmith 开源低代码平台并配置远程访问

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online