Linux动静态库开发基础:静态库与动态库的编译构建、链接使用及问题排查

Linux动静态库开发基础:静态库与动态库的编译构建、链接使用及问题排查

文 章 目 录

一、静 态 库1、背 景2、原 理3、静 态 库 的 流 程(1)编 写 者(2)使 用 者(3)简 化 gcc 编 译 选 项(4)myerrno 问 题(5)结 论

二、动 静 态 库1、编 写 者2、使 用 者3、加 载 找 不 到 动 态 库

三、总 结

💻作 者 简 介:曾 与 你 一 样 迷 茫,现 以 经 验 助 你 入 门 Linux。 💡个 人 主 页:@笑口常开xpr 的 个 人 主 页 📚系 列 专 栏:Linux 探 索 之 旅:从 命 令 行 到 系 统 内 核 ✨代 码 趣 语:静 态 库 是 装 满 工 具 的 箱 子,编 译 时 全 塞 程 序,方 便 但 沉;动 态 库 是 共 享 架,记 位 置,没 工 具 就 卡 壳。 💪代 码 千 行,始 于 坚 持,每 日 敲 码,进 阶 编 程 之 路。 📦gitee 链 接:gitee

在 Linux C/C++ 开 发 中,库 是 代 码 复 用 和 工 程 化 的 核 心。不 少 开 发 者 会 遇 到 源 码 泄 露、编 译 “找 不 到 头 文 件 / 库”、动 态 库 运 行 加 载 失 败 等 问 题,本 质 是 对 库 的 流 程 不 熟 悉。本 文 从 背 景 切 入,先 讲 静 态 库 的 原 理、制 作 与 使 用,再 讲 动 态 库 的 实 战 技 巧,帮 你 掌 握 库 的 全 流 程 应 用。

一、静 态 库

1、背 景

设 计 一 个 静 态 库 并 将 已 经 写 好 的 代 码 给 别 人 用。有 两 种 方 法:

把 源 文 件 给 他把 源 代 码 打 包 成 库,必 须 提 供 头 文 件。头 文 件 的 本 质 是 库 文 件 的 说 明 书。

libxxx.a - - - 静 态 链 接 libxxx.so - - - 动 态 链 接

2、原 理

3、静 态 库 的 流 程

(1)编 写 者

编 写 源 代 码(不 包 括 main 函 数)。

mymath.h

#pragma once

#include

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

#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

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 函 数。

#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

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。 修 改 后 的 代 码

#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

#pragma once //防止头文件被重复包含

#include

void log(const char*);

mylog.c

#include"mylog.h"

void log(const char* info)

{

printf("Warning:%s\n",info);

}

myprint.c

#include "myprint.h"

void Print()

{

printf("hello world!\n");

printf("hello world!\n");

printf("hello world!\n");

printf("hello world!\n");

}

myprint.h

#pragma once //防止头文件被重复包含

#include

void Print();

makefile

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 种 加 载 问 题 解 决 方 案)的 核 心 内 容。静 态 库 嵌 入 程 序、独 立 但 体 积 大,动 态 库 共 享 模 块、轻 量 但 需 依 赖 系 统,需 按 需 选 择。后 续 可 尝 试 封 装 通 用 模 块 为 库,或 研 究 版 本 管 理,进 一 步 提 升 开 发 效 率。

更多尼泊尔内容

奔驰新车首保多少公里保养一次?
3658188

奔驰新车首保多少公里保养一次?

🗓️ 09-14 👁️ 4580
精灵宝可梦怎么忘记秘传技能?教你快速清除秘传招式!
6代牛头1911可以连发吗
3658188

6代牛头1911可以连发吗

🗓️ 10-20 👁️ 5047
中国移动流量卡怎么用,通用流量和定向流量区别?
casino365sport365

中国移动流量卡怎么用,通用流量和定向流量区别?

🗓️ 10-21 👁️ 2289
单双排和灵活组排什么区别(灵活组排比单双排水平高吗)
注册送365元可提款

单双排和灵活组排什么区别(灵活组排比单双排水平高吗)

🗓️ 08-25 👁️ 3807
德国队世界杯备战集训“解密”
casino365sport365

德国队世界杯备战集训“解密”

🗓️ 07-04 👁️ 5366
开网吧需要什么证件
注册送365元可提款

开网吧需要什么证件

🗓️ 08-05 👁️ 8557
电脑上怎么看指定人的朋友圈? ( 电脑上怎么看别人的朋友圈 )
Excel批注:添加、显示/隐藏、修改、删除及更多高级操作