【c语言】linux下静态库和动态库制作

0 阅读3分钟

静态库制作

静态库(后缀 .a)是多个目标文件(.o)的归档文件,链接时会被完整拷贝到可执行程序中,生成的程序不依赖外部库即可运行。

  • 制作静态库相关工具
    • GCC:将源文件编译为目标文件(.o);
    • ar:GNU 归档工具,用于将多个 .o 文件打包为静态库(.a);
    • ranlib(可选):为静态库生成索引(现代 ar 已集成,加 -s 参数即可)。
  • 静态库命名规范
    • Linux 下静态库必须以 lib 开头,后缀为 .a

2.1、制作步骤

目录结构

.
├── include
│   └── fun.h
└── src
    └── fun.c

头文件,声明库中的函数

#ifndef _FUN_H
#define _FUN_H
int add(int a, int b);
#endif

源文件,实现函数

#include "fun.h"

int add(int a, int b)
{
	return a + b;
}

  • 源文件编译为目标文件
# -I 指定头文件位置
# -Wall 开启警告
gcc@gcc:~/2026/c/static/src$ gcc -c fun.c -o fun.o -I ../include -Wall
gcc@gcc:~/2026/c/static/src$ ls
fun.c  fun.o
  • 用ar工具将.o文件归档为.a静态库
# ar rcs 静态库名称 目标文件...
gcc@gcc:~/2026/c/static$ ar rcs ./lib/libfun.a ./src/fun.o 
gcc@gcc:~/2026/c/static$ ls ./lib/
libfun.a

ar 参数详解

  • r:将目标文件插入 / 替换到静态库中(若库已存在);
  • c:创建新的静态库(若不存在);
  • s:为静态库生成索引(等价于 ranlib,现代 ar 必加,否则链接时可能报错)。 验证静态库
# 查看静态库中的目标文件
gcc@gcc:~/2026/c/static/lib$ ar t libfun.a 
fun.o

# 查看静态库中的函数(nm 工具) nm libutil.a
gcc@gcc:~/2026/c/static/lib$ nm libfun.a 

fun.o:
0000000000000000 T add

2.2、使用静态库

制作完静态库后,需在编译测试程序时链接该库,核心是指定头文件路径库路径库名

#include <stdio.h>
#include "fun.h"

int main(void)
{
	int sum = add(1020);
	printf("sum = %d\n", sum);
	return 0;
}

使用静态库编译可执行程序

# -L 指定库位置
gcc@gcc:~/2026/c/static$ gcc main.c -o app -I ./include -L ./lib -lfun -Wall
gcc@gcc:~/2026/c/static$ ls
app  include  lib  main.c  src
gcc@gcc:~/2026/c/static$ ./app
sum = 30

2.3、使用注意事项

  • 静态库与动态库冲突:若系统中同时存在同名静态库(.a)和动态库(.so),GCC 默认优先链接动态库,强制链接静态库需加 -static 参数:
gcc main.c -o app -I ./include -L ./lib -lfun -static -Wall
  • 静态库中不能有重复的函数名(多个 .o 文件中定义同名函数),否则链接时会报 multiple definition of xxx 错误。
    • Linux 下的 .a 库无法在 Windows 下使用(需用 MinGW 交叉编译);
  • 32 位 GCC 编译的静态库不能用于 64 位程序(加 -m32/-m64 指定架构);
  • 若多个静态库存在依赖关系,链接时需按 “依赖逆序” 排列,例如:libA.a 依赖 libB.a,则编译命令为:
gcc main.c -o app -LA -LB -lA -lB # 先链接依赖者,再链接被依赖者

动态库制作

GCC 制作动态库(也叫共享库,后缀 .so)是 Linux 开发中实现代码复用、动态更新的核心技能。动态库在程序运行时才被加载,不会被拷贝到可执行程序中,因此生成的程序体积小,且库文件更新后无需重新编译程序。

  • 文件命名规范:Linux 下以 lib 开头,后缀 .so(如 libutil.so),通常还会带版本号(如 libutil.so.1.0
  • 编译参数:必须加 -fPIC(生成位置无关代码)和 -shared(生成动态库而非可执行程序)
  • 运行依赖:程序运行时需能找到动态库(通过 LD_LIBRARY_PATH 或系统库路径)
  • 优势:程序体积小、库更新无需重新编译程序、多个程序可共享同一个库;
  • 劣势:部署时需同时发布库文件,运行时依赖库存在且版本兼容;
  • 工具:
    • GCC:编译源文件为位置无关的目标文件(.o),并链接为动态库;
    • ldd:查看可执行程序依赖的动态库(排查 “找不到库” 问题);
    • ldconfig:配置系统动态库缓存(永久生效库路径)。

3.1、制作步骤

代码同静态库代码;

  • 编译为位置无关的目标文件
gcc@gcc:~/2026/c/shard/src$ gcc -c fun.c -o fun.o -I ../include -fPIC -Wall
gcc@gcc:~/2026/c/shard/src$ ls
fun.c  fun.o

关键参数

  • -fPIC:生成位置无关代码(动态库必需,否则链接时会报 relocation R_X86_64_PC32 against symbol xxx can not be used when making a shared object 错误);
  • -c:仅编译生成目标文件,不链接;
  • -Wall:开启警告,确保库代码无语法问题。

链接生成动态库

gcc@gcc:~/2026/c/shard$ gcc -shared -o ./lib/libfun.so ./src/fun.o 
gcc@gcc:~/2026/c/shard$ ls ./lib/
libfun.so

参数详解

  • -shared:指定生成动态库(而非可执行程序),必须加;
  • -o libutil.so:指定动态库输出文件名(遵循 libxxx.so 规范)。

3.2、使用动态库

gcc@gcc:~/2026/c/shard$ gcc main.c -o app -I ./include -L ./lib -lfun -Wall
gcc@gcc:~/2026/c/shard$ ls
app  include  lib  main.c  src
gcc@gcc:~/2026/c/shard$ ./app 
./app: error while loading shared libraries: libfun.so: cannot open shared object file: No such file or directory

参数详解

  • -I./include:指定头文件 util.h 所在目录;
  • -L./:指定动态库 libutil.so 所在目录(当前目录);
  • -lutil:链接 libutil.so-l 后接库名,自动补全 lib.so);
  • 与静态库链接命令完全一致,GCC 会自动优先链接动态库(若同时存在 .a.so)。

3.3、解决动态库加载问题

方法 1:临时指定动态库路径(适合测试)通过 LD_LIBRARY_PATH 环境变量指定动态库所在目录:

gcc@gcc:~/2026/c/shard$ export LD_LIBRARY_PATH=./lib:$LD_LIBRARY_PATH
gcc@gcc:~/2026/c/shard$ ./app 
sum = 30

方法 2:永久添加库路径 将动态库路径写入系统动态库配置文件:

# 1. 将库路径添加到 /etc/ld.so.conf.d/(推荐) 
echo "lib路径" | sudo tee /etc/ld.so.conf.d/util.conf

# 2. 更新动态库缓存 sudo ldconfig

方法 3:编译时指定运行库路径(推荐) 通过 -Wl,-rpath 参数,将动态库路径写入可执行程序(永久生效):

gcc@gcc:~/2026/c/shard$ gcc main.c -o app -I./include -L./lib -lfun -Wl,-rpath=./lib -Wall
gcc@gcc:~/2026/c/shard$ ./app
sum = 30

3.4、动态库关键调试工具

  • 查看程序依赖的动态库(ldd)
gcc@gcc:~/2026/c/shard$ ldd app
	linux-vdso.so.1 (0x00007492da43b000)
	libfun.so => ./lib/libfun.so (0x00007492da42b000)
	libc.so.6 => /lib/x86_64-linux-gnu/libc.so.6 (0x00007492da200000)
	/lib64/ld-linux-x86-64.so.2 (0x00007492da43d000)
  • 若显示 libfun.so => not found,说明库路径未配置;
  • 若显示 libfun.so => /usr/lib/libfun.so,说明系统优先加载了其他版本的库。
  • 查看动态库中的函数(nm)
gcc@gcc:~/2026/c/shard$ nm -D ./lib/libfun.so 
00000000000010f9 T add
                 w __cxa_finalize
                 w __gmon_start__
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
  • 检查动态库兼容性
gcc@gcc:~/2026/c/shard$ file ./lib/libfun.so 
./lib/libfun.so: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, BuildID[sha1]=8a31f8d0979be0b8dc1966da76954ba33642038a, not stripped

[! 注意] 64 位程序无法加载 32 位动态库(加 -m32/-m64 指定编译架构)。