文 章 目 录
- [一、静 态 库](#一、静 态 库)
-
- [1、背 景](#1、背 景)
- [2、原 理](#2、原 理)
- [3、静 态 库 的 流 程](#3、静 态 库 的 流 程)
-
- [(1)编 写 者](#(1)编 写 者)
- [(2)使 用 者](#(2)使 用 者)
- [(3)简 化 gcc 编 译 选 项](#(3)简 化 gcc 编 译 选 项)
- [(4)myerrno 问 题](#(4)myerrno 问 题)
- [(5)结 论](#(5)结 论)
- [二、动 静 态 库](#二、动 静 态 库)
-
- [1、编 写 者](#1、编 写 者)
- [2、使 用 者](#2、使 用 者)
- [3、加 载 找 不 到 动 态 库](#3、加 载 找 不 到 动 态 库)
- [三、总 结](#三、总 结)
💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 Linux。
💡个 人 主 页:@笑口常开xpr 的 个 人 主 页
📚系 列 专 栏:Linux 探 索 之 旅:从 命 令 行 到 系 统 内 核
✨代 码 趣 语:静 态 库 是 装 满 工 具 的 箱 子,编 译 时 全 塞 程 序,方 便 但 沉;动 态 库 是 共 享 架,记 位 置,没 工 具 就 卡 壳。
💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。
📦gitee 链 接:gitee

在 Linux C/C++ 开 发 中,库 是 代 码 复 用 和 工 程 化 的 核 心。不 少 开 发 者 会 遇 到 源 码 泄 露、编 译 "找 不 到 头 文 件 / 库"、动 态 库 运 行 加 载 失 败 等 问 题,本 质 是 对 库 的 流 程 不 熟 悉。本 文 从 背 景 切 入,先 讲 静 态 库 的 原 理、制 作 与 使 用,再 讲 动 态 库 的 实 战 技 巧,帮 你 掌 握 库 的 全 流 程 应 用。
一、静 态 库
1、背 景
设 计 一 个 静 态 库 并 将 已 经 写 好 的 代 码 给 别 人 用。有 两 种 方 法:
- 把 源 文 件 给 他
- 把 源 代 码 打 包 成 库,必 须 提 供 头 文 件。头 文 件 的 本 质 是 库 文 件 的 说 明 书。
libxxx.a - - - 静 态 链 接
libxxx.so - - - 动 态 链 接
2、原 理

3、静 态 库 的 流 程
(1)编 写 者
- 编 写 源 代 码(不 包 括 main 函 数)。
mymath.h
javascript
#pragma once
#include<stdio.h>
extern int myerrno;
int add(int x,int y);
int sub(int x,int y);
int mul(int x,int y);
int div(int x,int y);
mymath.c
javascript
#include"mymath.h"
int myerrno = 0;
int add(int x,int y)
{
return x + y;
}
int sub(int x,int y)
{
return x - y;
}
int mul(int x,int y)
{
return x * y;
}
int div(int x,int y)
{
if(y == 0)
{
myerrno = 1;
return -1;
}
return x / y;
}
makefile
javascript
lib=libmymath.a
$(lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^
.PHONY:clean
clean:
rm -rf *.o *.a lib
.PHONY:output
output:
mkdir -p lib/include
mkdir -p lib/mymathlib
cp *.h lib/include
cp *.a lib/mymathlib
这 里 不 写 $@
的 原 因 是 因 为 gcc 可 以 将 mymath.h
编 译 成 和 源 文 件 名 字 相 同 的 .o
文 件。
ar
是 生 成 静 态 库 的 1 个 命 令,可 以 将 所 有 的 .o
文 件 打 包 形 成 .a
文 件,-rc
表 示 将 所 有 的 .o
放 在 目 标 文 件 .a
中,如 果 不 存 在 就 创 建,如 果 存 在 就 替 换。
- 编 译 生 成
.a
和.o
文 件。
- 将 生 成 的 文 件 打 包 进 文 件 夹 中。
- 将 生 成 的
lib
文 件 夹 打 包 压 缩。
(2)使 用 者
- 下 载 并 解 压 静 态 库 的 压 缩 包
- 编 写 main 函 数。
javascript
#include "mymath.h"
int main()
{
int n = div(10,0);
printf("10/0=%d,errno=%d\n",n,myerrno);
return 0;
}
- 编 译 代 码
没 有 找 到 头 文 件
原 因
编 译 器 会 在 默 认 路 径 和 当 前 目 录 下(和 源 代 码 在 同 一 级 路 径 下) 寻 找 头 文 件。
解 决 方 法
方 法 1
gcc main.c -I + 目 录
:编 译 器 会 去 指 定 目 录 下 寻 找 头 文 件。
方 法 2
#include "lib/include/mymath.h"
可 以 在 代 码 中 包 含 路 径。
这 里 推 荐 使 用 方 法 1。
链 接 错 误(找 不 到 静 态 库)
下 图 中 的 错 误 是 链 接 错 误,原 因 是 因 为 以 .o
结 尾 的 一 般 是 链 接 错 误。
-c:编 译 到 目 标 代 码
gcc -c 编 译 通 过,只 能 说 明 代 码 在 语 法 和 基 本 编 译 规 则 上 没 有 问 题,它 不 检 查 链 接 错 误。
-L
静 态 库 的 存 储 路 径。
没 有 包 含 .a 文 件
-l
找 到 .a
文 件,这 里 是 静 态 库 的 真 实 名 字,建 议 -l 和 库 的 真 实 名 字 相 连 接,二 者 之 间 没 有 空 格。
库 的 真 实 名 字
去 掉 前 缀 和 后 缀。
(3)简 化 gcc 编 译 选 项
- 直 接 将 头 文 件 和 静 态 库 放 进 系 统 文 件 夹。这 是 库 的 安 装。这 里 不 建 议 不 要 将 头 文 件 和 静 态 库 放 入 系 统 中。
需 要 指 明 静 态 库 的 真 实 名 字,才 能 编 译 成 功。
- 使 用 软 链 接
- 使 用 makefile
javascript
main:main.c
gcc main.c -I ./lib/include/ -L ./lib/mymathlib/ -lmymath -o main
.PHONY:clean
clean:
rm -f main
(4)myerrno 问 题

上 面 的 errno 不 是 -1 的 原 因 是 因 为 c 语 言 的 实 例 化 是 从 右 向 左 实 例 化 的。即 printf("10/0=%d,errno=%d\n",div(10,0),myerrno);
这 句 代 码 中 先 调 用 myerrno 然 后 使 用 div。调 用 的 顺 序 错 误 正 确 的 顺 序 是 在 调 用 myerrno 时 应 使 用 div,然 后 使 用 myerrno。
修 改 后 的 代 码
javascript
#include "mymath.h"
int main()
{
int n = div(10,0);
printf("10/0=%d,errno=%d\n",n,myerrno);
return 0;
}

(5)结 论
-
第 3 方 库 使 用 的 时 候 必 须 使 用
-l
选 项 -
ldd
可 以 查 看 是 否 是 动 态 链 接 还 是 静 态 链 接。如 下 图 gcc 采 用 的 是 动 态 链 接,因 为 文 件 是 以
.so
结 尾 的。
-
gcc 默 认 是 动 态 链 接,如 果 只 提 供 静 态 链 接,gcc 只 能 对 该 库 使 用 静 态 链 接。ldd 的 输 出 反 映 的 是 整 个 程 序 是 否 依 赖 动 态 库,而 非 单 个 库 的 链 接 方 式。上 面 使 用 的 是 静 态 库,gcc 对 静 态 库 采 用 静 态 链 接,这 里 没 有 显 示 静 态 库 的 链 接。但 对 可 执 行 程 序 来 讲,程 序 中 还 有 其 他 库(如 C 标 准 库 libc)使 用 了 动 态 链 接(默 认 行 为),整 个 程 序 仍 然 是 "动 态 链 接 的 可 执 行 文 件"。
-
当 同 一 库 的 动 态 版 本(.so)和 静 态 版 本(.a)同 时 存 在 时,gcc 默 认 会 优 先 选 择 动 态 链 接 方 式。
-
若 要 强 制 使 用 静 态 链 接,可 在 编 译 时 添 加 -static 选 项。不 过 需 要 注 意,-static 并 非 绝 对 强 制 的 指 令 - - - 它 的 作 用 是 告 知 gcc 尽 量 采 用 静 态 链 接,但 在 某 些 情 况 下,gcc 可 能 仍 无 法 实 现 完 全 的 静 态 链 接。
二、动 静 态 库
1、编 写 者
- 编 写 代 码
gcc
mylog.h
javascript
#pragma once //防止头文件被重复包含
#include<stdio.h>
void log(const char*);
mylog.c
javascript
#include"mylog.h"
void log(const char* info)
{
printf("Warning:%s\n",info);
}
myprint.c
javascript
#include "myprint.h"
void Print()
{
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
printf("hello world!\n");
}
myprint.h
javascript
#pragma once //防止头文件被重复包含
#include<stdio.h>
void Print();
makefile
javascript
dy-lib=libmymethod.so
static-lib=libmymath.a
# 同时生成动态库和静态库
.PHONY:all
all: $(dy-lib) $(static-lib)
# 生成静态库
$(static-lib):mymath.o
ar -rc $@ $^
mymath.o:mymath.c
gcc -c $^
# 生成动态库
$(dy-lib):mylog.o myprint.o
gcc -shared -o $@ $^
mylog.o:mylog.c
gcc -fPIC -c $^
myprint.o:myprint.c
gcc -fPIC -c $^
# 清理静态库文件
.PHONY:clean
clean:
rm -rf *.o *.a *.so mylib
# 打包
.PHONY:output
output:
mkdir -p mylib/include
mkdir -p mylib/lib
cp *.h mylib/include
cp *.a mylib/lib
cp *.so mylib/lib
- 编 译 代 码,将 .c 文 件 编 译 成 .o 文 件。
fPIC :产 生 位 置 无 关 码。
- 生 成 动 态 库
.so
表 示 动 态 库。
-shared
此 选 项 表 示 不 生 成 可 执 行 程 序,将 尽 量 使 用 动 态 库,所 以 生 成 文 件 比 较 小,但 是 需 要 系 统 由 动 态 库 -O0、-O1、-O2、-O3 编 译 器 的 优 化 选 项 的 4 个 级 别,-O0 表 示 没 有 优 化,-O1 为 缺 省 值,-O3 优 化 级 别 最 高。
2、使 用 者
- 编 译 代 码(包 含 动 静 态 库)
- 运 行 代 码
有 ldd 和 a.out 可 以 看 出 生 成 的 可 执 行 程 序 为 动 态 链 接 的。
gcc 编 译 时 的 路 径 是 编 译 器,还 需 要 让 系 统(加 载 器) 明 白 动 态 库 在 哪 里。
3、加 载 找 不 到 动 态 库
-
将 动 态 库 拷 贝 到 系 统 路 径(/lib64 或 者 /usr/lib64/) 下。实 际 情 况,我 们 使 用 的 库 都 是 别 人 成 熟 的 库,都 采 用 直 接 安 装 到 系 统 的 方 式。
-
建 立 软 链 接
-
将 自 己 的 库 所 在 的 路 径 添 加 到 系 统 的 环 境 变 量 中。使 用
echo $LD_LIBRARY_PATH
搜 索 用 户 自 定 义 的 库 路 径。
xshell 每 次 重 启 都 会 重 新 加 载 环 境 变 量,可 以 将
LD_LIBRARY_PATH
这 个 环 境 变 量 添 加 到vim ~/.bash_profile
,来 解 决 这 个 问 题。
-
在
/etc/ld.so.conf.d
这 是 系 统 维 护 动 态 库 时 放 的 路 径,建 立 自 己 的 动 态 库 路 径 的 配 置 文 件,然 后 使 用ldconfig
重 新 加 载 即 可。
(1)切 换 到 root 身 份 并 进 入 到
/etc/ld.so.conf.d
这 个 路 径 下,创 建 以.conf
结 尾 的 文 件 并 添 加 路 径,这 种 方 法 和 xshell 是 否 关 闭 没 有 影 响。
(2)使 用
ldconfig
重 新 加 载 文 件(3)成 功 执 行 可 执 行 程 序
(4)如 果 不 想 使 用 这 种 方 法,可 以 删 除 刚 才 新 建 的 文 件,然 后 使 用
ldconfig
重 新 加 载 文 件 即 可。
三、总 结
本 文 覆 盖 了 静 态 库(.o 打 包、Makefile 构 建、-I/-L/-l 链 接)与 动 态 库(-fPIC/-shared 编 译、4 种 加 载 问 题 解 决 方 案)的 核 心 内 容。静 态 库 嵌 入 程 序、独 立 但 体 积 大,动 态 库 共 享 模 块、轻 量 但 需 依 赖 系 统,需 按 需 选 择。后 续 可 尝 试 封 装 通 用 模 块 为 库,或 研 究 版 本 管 理,进 一 步 提 升 开 发 效 率。
