静态库和动态库的制作

一、库文件介绍

• 定义:库文件是一组预先编译好的函数、类或数据的集合,以文件的形式存在,用于被其他程序调用;

• 本质:库文件是一种可执行代码的二进制形式,是代码复用的一种方式,将常用功能封装起来,方便 开发者在不同项目中使用;

• 作用:

提高开发效率:避免重复编写相同功能的代码,减少开发时间;

• 便于代码维护:集中管理功能模块,修改和升级库时,只需更新库文件,无需修改使用库的程序;

• 保护代码隐私:可以将核心代码封装在库中,只提供接口供外部使用,隐藏实现细节;

库的分类:静态库和共享库(动态库) 在不同平台中都存在大量的库,不同平台(如 Linux vs Windows)的指令集、二进制格式、系统调用不同, 导致库无法跨平台直接使用(比如 Linux 的 .so 不能在 Windows 上运行);

• linux平台下库文件命名:

• 静态库命名规则为:libxxx.a(以lib为前缀+自定义库名+.a后缀);

• 动态库命名规则为:libxxx.so(以lib为前缀+自定义库名+.so后缀);

若涉及到版本管理,动态库命名可扩展为:libxxx.so.x.y.z x 为主版本号,不兼容旧版;y 为次版本号,兼容旧版;z 为发行版本号,修复问题不影响接口

二、静态库的介绍和制作

1.静态库介绍

定义:静态库是在程序编译链接阶段,其代码会被完整地"复制"到最终生成的可执行文件中,成为可执行 文件的一部分,编译完成后,静态库与可执行文件"绑定",运行时无需额外依赖;

• 特点:

• 可执行文件独立运行,不依赖外部库文件;

• 静态库会使可执行文件体积较大,但是效率比较高;

• 若静态库更新,需重新编译链接程序;

• 静态库文件格式: • windows平台下为.lib;Linux平台下为.a;Mac平台下为.a;

2.制作流程

准备源文件:编写实现库功能的源文件(如 .c或 .cpp);

编译生成目标文件:使用编译器将源文件编译成目标文件(如 .o或.obj);

示例:gcc -c 源文件.c -o 目标文件.o

打包生成静态库:使用归档工具将目标文件打包成静态库;

示例:ar -cr 静态库.a 目标文件.o

编译主程序并链接静态库,生成可执行文件

示例:gcc 目标文件.o -l库名 -L库路径 -o 可执行文件

说明:如果库名是libmyadd.a,则库名是myadd,编译器会自动拼接lib前缀和.a后缀进行查找库文件;

3.核心选项

gcc常用选项

选项 作用 示例命令
-c 只编译不链接,生成目标文件(.o gcc -c add.c -o add.o
-o 指定输出文件名称 gcc -c main.c -o main.o
-l 指定要链接的库名(省略 lib 前缀和 .a/.so 后缀) gcc main.c -lmyadd -o main
-L 指定库文件的搜索路径 gcc main.c -L./lib -lmyadd -o main

ar工具常用选项

选项 作用 示例命令
-c 创建新的归档文件(静态库 .a ar -cr libmyadd.a add.o
-r 将目标文件添加到归档文件中(替换已存在的同名文件) ar -cr libmyadd.a add.o

4.制作并使用静态库示例代码

cs 复制代码
// add.h
#ifndef ADD_H
#define ADD_H
int add(int a, int b);
#endif



// add.c
#include "add.h"
int add(int a, int b) {
    return a + b;
}



// main.c
#include <stdio.h>
#include "add.h"
int main() {
    printf("1 + 2 = %d\n", add(1, 2));
    return 0;
}

三、动态库的介绍和制作

1.动态库介绍

定义:

动态库也称之为共享库,是在程序运行时才被加载到内存中,可以被多个可执行文件共享使用, 为它们提供函数、数据等资源;

• 特点:

• 可执行文件运行时,需依赖系统中存在的动态库;

• 可执行文件体积小,有效节省磁盘和内存空间;

• 更新灵活,只要接口不变,无需重新编译依赖它的可执行文件;

• 动态库文件格式: • windows平台下为.dll;Linux平台下为.so;Mac平台下为.dylib;

2.制作流程

• 准备源文件:编写实现库功能的源文件(如 .c或 .cpp);

•编译生成与位置无关的目标文件: 使用编译器编译源文件时,添加相关选项(如GCC的-fPIC选项)生成位置无关代码(PIC)的目标文件; 示例:gcc -c -fPIC 源文件.c -o 目标文件.o

• 链接生成动态库:使用编译器将目标文件链接为动态库,多个程序可共享同一份动态库文件,节省内存;

示例:gcc -shared 目标文件.o -o 动态库.so

• 编译时通过-L指定路径、-l指定库名生成可执行文件; 示例:gcc 源文件.c -l库名 -L库路径 -o 可执行文件

3.Linux 系统中,动态库(.so 文件)的三种运行时加载方法

方法 1:临时设置 LD_LIBRARY_PATH 环境变量

这是最简单、最常用的临时方法,适合调试和测试。

bash 复制代码
bash
运行
# 将当前目录加入动态库搜索路径
export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH

$PWD:表示当前工作目录,也可以换成你的库文件所在路径(比如 ./lib)。

特点:
临时生效,只对当前终端会话有效,关闭终端后失效。
适合开发调试,不会影响系统全局配置。

方法 2:修改 /etc/ld.so.conf 系统配置文件

这是永久生效的全局配置方法,所有用户和程序都能使用。

编辑配置文件,添加库路径:

bash 复制代码
bash
运行
sudo vim /etc/ld.so.conf

在文件中添加你的动态库路径,比如:

bash 复制代码
text
/home/hqyj/dynamic_library

更新系统缓存,让配置生效:

bash 复制代码
bash
运行
sudo ldconfig

特点:
永久生效,重启后依然有效。
配置一次后,所有程序都能找到这个路径下的动态库。

方法3 将动态库放到系统标准路径

这是最 "正统" 的方法,把库文件放到系统默认的库目录里。

-系统级库:放在 /usr/lib

-用户自定义库:放在 /usr/local/lib

bash 复制代码
# 示例:将 libmysub.so 复制到 /usr/local/lib
sudo cp libmysub.so /usr/local/lib
# 更新系统缓存
sudo ldconfig

特点:
永久生效,系统启动时就会加载这些路径下的库。
适合安装第三方库,不推荐频繁修改,避免污染系统目录

对比

方法 生效范围 永久 / 临时 适用场景
LD_LIBRARY_PATH 当前终端会话 临时 开发调试、临时运行程序
修改 /etc/ld.so.conf 系统全局 永久 长期使用的库、需要所有用户访问
放到 /usr/lib/usr/local/lib 系统全局 永久 安装第三方库、系统级依赖

实操建议

开发调试阶段:优先用 export LD_LIBRARY_PATH=PWD:LD_LIBRARY_PATH,简单又不会影响系统。

长期部署:用修改 /etc/ld.so.conf 或放到 /usr/local/lib 的方法,保证程序在任何环境下都能正常运行。

4.核心选项

a.GCC 动态库制作与使用命令速查表

阶段 工具 选项 作用 示例
编译阶段 gcc -c 只编译不链接,生成目标文件 .o gcc -c add.c -o add.o
-fPIC 生成位置无关代码,动态库必需 gcc -c -fPIC add.c -o add.o
-o 指定输出文件名(目标文件或库) gcc -c -fPIC add.c -o add.o
链接阶段 gcc -shared .o 链接为动态库 .so gcc -shared add.o -o libmyadd.so
使用阶段 gcc -L 指定动态库的搜索路径 gcc main.c -lmyadd -L./lib -o main
-l 指定要链接的动态库名(省略 lib.so gcc main.c -lmyadd -L. -o main
系统工具 ldconfig - 更新系统动态库缓存 sudo ldconfig

b.关键概念深度解析

1. -fPIC 是什么?

全称:Position-Independent Code(位置无关代码)

作用:生成的目标文件中,所有地址都是相对地址,这样动态库可以被加载到内存的任意位置运行,而不是固定在某个地址。

注意:制作动态库时必须加 -fPIC,否则生成的 .o 文件无法链接成动态库。
2. -shared 选项

作用:告诉 GCC 生成动态库,而不是可执行文件。

配合 -o 使用,指定输出的库文件名,格式一般为 libxxx.so(lib 前缀是约定俗成的)。
3. -L 和 -l 的区别

-L:告诉编译器在哪个目录找库文件。

-l:告诉编译器要链接哪个库文件,库名是去掉 lib 前缀和 .so 后缀的部分。

例如 libmyadd.so,链接时写 -lmyadd。

c.示例

cs 复制代码
// add.h
#ifndef ADD_H
#define ADD_H
int add(int a, int b);
#endif

// add.c
#include "add.h"
int add(int a, int b) {
    return a + b;
}

// main.c
#include <stdio.h>
#include "add.h"
int main() {
    printf("1 + 2 = %d\n", add(1, 2));
    return 0;
}
  1. 编译阶段(生成带 -fPIC 的目标文件)
bash 复制代码
bash
运行
gcc -c -fPIC add.c -o add.o
  1. 链接阶段(生成动态库)
bash 复制代码
bash
运行
gcc -shared add.o -o libmyadd.so
  1. 使用阶段(编译主程序并链接动态库)
bash 复制代码
bash
运行
gcc main.c -L. -lmyadd -o main
  1. 运行阶段(让系统找到动态库)
bash 复制代码
# 临时设置环境变量(开发调试用)
export LD_LIBRARY_PATH=$PWD:$LD_LIBRARY_PATH
./main

d.ldconfig 命令

作用:更新系统动态库缓存,让系统能识别新添加的动态库。

使用场景:当你把 .so 文件放到 /usr/lib 或 /usr/local/lib 等系统路径后,需要执行 sudo ldconfig 让系统识别它。

四、动静态库的区别

1.核心区别

静态库(.a):编译时直接打包进可执行文件

动态库(.so):运行时才加载,不打包进程序

2.对比表

对比项 静态库(.a) 动态库(.so)
后缀名 .a(Linux) .so(Linux)
加载时机 编译链接时 程序运行时
是否打包进程序 ✅ 是,全部复制进去 ❌ 否,只留引用
可执行文件大小
运行时依赖库 ❌ 不依赖,可直接运行 ✅ 必须依赖,找不到就报错
更新库文件 必须重新编译程序 直接替换库,程序不用重新编译
内存占用 多个程序用同一个库,会加载多份 多个程序共享同一份,省内存
制作命令 ar cr libxxx.a x.o gcc -shared -fPIC -o libxxx.so x.o
运行速度 稍快(无加载开销) 稍慢(运行时加载)

最关键的 4 个核心区别

  1. 打包方式不同

静态库:代码直接塞进你的可执行文件

动态库:代码留在外面,程序运行时去 "借" 用

  1. 更新方式不同

静态库:库改了 → 程序必须重新编译

动态库:库改了 → 直接替换 .so 文件,程序不用动

  1. 内存占用不同

静态库:10 个程序用 → 10 份库代码

动态库:10 个程序用 → 内存只存 1 份,大家共享

  1. 运行依赖不同

静态库:发给别人就能直接跑

动态库:必须保证对方机器上有这个 .so 库

3.怎么选择用哪个?

✔ 用 静态库

不想让程序依赖外部文件

程序要发给别人直接运行

小工具、简单程序

✔ 用 动态库

库可能经常更新

多个程序共用同一个库(节省空间)

系统级大型程序、驱动、插件

4.制作命令对照表

静态库

bash 复制代码
gcc -c add.c -o add.o
ar cr libadd.a add.o
gcc main.c -L. -ladd -o main



*\编译源文件生成目标文件:
gcc -c 源文件.c -o 目标文件.o
(例:gcc -c add.c -o add.o)
使用 ar 命令创建静态库:
ar crs 静态库名.a 目标文件.o
(例:ar crs libmyadd.a add.o)
编译程序时链接静态库:
gcc 主程序.c -L库路径 -l库名 -o 可执行文件
(例:gcc main.c -L. -lmyadd -o main)
执行程序(无需额外配置):
./可执行文件
\*

动态库

bash 复制代码
gcc -c -fPIC add.c -o add.o
gcc -shared -o libadd.so add.o
gcc main.c -L. -ladd -o main
export LD_LIBRARY_PATH=./
./main



* 编译源文件生成位置无关目标文件:
gcc -c -fPIC 源文件.c -o 目标文件.o
(例:gcc -c -fPIC add.c -o add.o)

使用 gcc 创建动态库:
gcc -shared 目标文件.o -o 动态库名.so
(例:gcc -shared add.o -o libmyadd.so)

编译程序时链接动态库:
gcc 主程序.c -L库路径 -l库名 -o 可执行文件
(例:gcc main.c -L. -lmyadd -o main)

设置临时环境变量(运行必须):
export LD_LIBRARY_PATH=./:$LD_LIBRARY_PATH

执行程序:
./可执行文件
*

5.对照

bash 复制代码
======== 静态库流程 ========
gcc -c add.c -o add.o
ar crs libmyadd.a add.o
gcc main.c -L. -lmyadd -o main
./main

======== 动态库流程 ========
gcc -c -fPIC add.c -o add.o
gcc -shared add.o -o libmyadd.so
gcc main.c -L. -lmyadd -o main
export LD_LIBRARY_PATH=./
./main

五、库制作常见问题及解决方案

方法 1:拷贝库到系统目录(/usr/lib或/lib)

原理:系统默认搜索这些目录;

优点:简单直接,无需额外配置;

缺点:可能污染系统库,不适合多版本管理;

方法 2:设置LD_LIBRARY_PATH环境变量

原理:告诉系统额外的库搜索路径;

优点:灵活,可临时指定路径;

缺点:仅当前终端有效,关闭后失效;

方法 3:修改/etc/ld.so.conf并执行ldconfig

原理:将库路径写入配置文件,刷新系统库缓存;

优点:永久生效,支持目录下所有库;

缺点:需管理员权限,修改后需刷新缓存;

bash 复制代码
* 静态库常见问题:
1. undefined reference → 顺序错误,库放最后
2. cannot find -lxxx → 库名必须 libxxx.a,加 -L.

* 动态库常见问题:
1. 编译失败 → 缺少 -fPIC
2. 运行失败 → 设置 LD_LIBRARY_PATH=./
3. 找不到库 → 永久配置 /etc/ld.so.conf
*

找不到库 → 看名字是否 libxxx.a/so

链接报错 → 库必须放命令最后

动态库不能运行 → 加 export LD_LIBRARY_PATH=./

动态库编译失败 → 加 -fPIC

相关推荐
程序员老邢2 小时前
【产品底稿 07】商助慧 Admin 运维模块落地:从 “能跑” 到 “能运维”,3 个页面搞定日常排障
java·运维·经验分享·spring boot·后端
a1117762 小时前
高斯泼溅 (Gaussian Splatting) 的 Three.js 实现
开发语言·javascript·ecmascript
成都渲染101云渲染66662 小时前
云渲染全面支持3dsMax 2027,高效渲染体验升级
开发语言·前端·javascript
一口Linux2 小时前
Linux C编程 | 从0实现telnet获取程序终端控制权
linux·运维·c语言
willhuo2 小时前
Certbot工具在CentOS 7.9上申请和配置SSL证书完整教程
linux·centos·ssl
向往着的青绿色3 小时前
Java反序列化漏洞(持续更新中)
java·开发语言·计算机网络·安全·web安全·网络安全·网络攻击模型
小短腿的代码世界3 小时前
Qt跨进程通信在交易系统中的应用:让策略引擎与风控模块在毫秒级握手
开发语言·qt
zhangrelay3 小时前
三分钟云课实践速通--大学物理--python 版
linux·开发语言·python·学习·ubuntu·lubuntu
MegaDataFlowers4 小时前
调用Service层操作数据
java·开发语言