【Linux】库的制作与使用(1):库的概念及动静态库

目录

[一 学习前言](#一 学习前言)

[二 什么是库](#二 什么是库)

[三 如何制作库](#三 如何制作库)

[四 如何使用库](#四 如何使用库)

[1 使用第三方库](#1 使用第三方库)

[2 动静态库使用区别](#2 动静态库使用区别)

[3 Linux 下让 OS 找到动态库的 4 种方法](#3 Linux 下让 OS 找到动态库的 4 种方法)

[4 三个问题](#4 三个问题)

[五 动态库和静态库](#五 动态库和静态库)

[1 动态库](#1 动态库)

(1)动态库生成

(2)动态库使用

(3)库运行搜索路径

(4)使用外部库

[2 静态库](#2 静态库)

[(1) 静态库生成](#(1) 静态库生成)

(2)静态库使用


一 学习前言

我们学习这部分内容,主要分三个部分:

  1. 如和制作库---站在库的制作者角度

2.如何使用库---库的使用者角度

3.库的制作原理和加载原理---ELF


二 什么是库

库是写好的现有的、成熟的、可以复用的代码。现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。 本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。

库有两种:

  • 静态库:.a[Linux]、.lib[Windows]

  • 动态库:.so[Linux]、.dll[Windows]

库类型 Linux 后缀 Windows 后缀
动态库 .so .dll
静态库 .a .lib

为什么要有库?

提高开发者的效率 例如:frintf不用自己手动在写一个;可以提高代码的健壮性和鲁棒性

库的本质:.o文件的集合

根据打包方式的不同,我们把库分为静态库动态库

当你想把自己写的方法 / 功能提供给他人使用时,有两种典型路径:

开源方式:直接提供头文件 + 源文件,对方可查看、修改源代码。

闭源方式:提供头文件 + .o 目标文件,隐藏源代码实现;进一步将多个 .o 文件打包成库文件,方便分发和使用。

Linux、Windows、macOS 等平台的标准 C 语言库无法直接互通


三 如何制作库

本质上的库,就是把文件编译成.o文件后打包--->要求编译器支持.o并打包,支持对库做连接

gcc - c * .c:把.c文件编译成同名.o文件

把.o文件打包成静态库:ar命令---把文件归档,打包

两个选项--- -r(replace) -c(create)

ar-rc xxx.a * .o :如果某些.o文件在.a文件中已经打包过了,-r就表示替换它,保证用最新的 ;如果不存在,就用-c创造他 不存在就新建,存在就替换

库文件在命名上有特点:

1 必须以lib开头 2 以.so/.a结尾 3 中间部分才是真正的库名字

Eg:libxxx.so或libxxx.a


四 如何使用库

1 使用第三方库

如果想使用静态库,必须明确告诉别人静态库在哪一个路径下

为什么我们在使用c语言的库方法时,编译的时候没有加过这些选项?

因为gcc编译器专门用来编译c语言的,它默认就带这些选项(-c -l)

但是我们自己定义的库,gcc并不知道这个库时干什么的,所以带选项是为了明确告诉gcc,库是做什么用的

凡是使用第三方库(操作系统,语言自带的库),未来都必须明确告诉gcc,库的名字和路径

我们平时编译的时候,没有告诉gcc路径,就表示系统一定有默认路径

gcc 会在以下目录自动搜索库文件:
/lib64
/usr/lib64
(64 位系统)其他标准路径如 /lib、/usr/lib
如果把你写的库拷贝到这个路径下,就不用告诉gcc路径了----这个工作叫做库的安装;如果你的库放在自定义目录,就需要用 -L 手动添加路径

打包并压缩:

gcc的选项:

cpp 复制代码
gcc -o myexe main.c -I./myc/include -L./myc/lib -lmyc

-I./myc/include:指定头文件搜索路径(让 gcc 找到 my_stdio.h、my_string.h)
-L./myc/lib:指定库文件搜索路径(让 gcc 找到 libmyc.a)
-lmyc:指定要链接的库名(gcc 会自动补全为 libmyc.a 或 libmyc.so

能不能把库安装到系统里?(安装方法:头文件拷贝到usr/include目录,库文件拷贝到/lib64目录

**不建议!**建议把库放在自己的目录结构

库能不能出现main函数?

不能!

main 函数是可执行程序的入口,一个程序只能有一个 main 函数。

如果库中包含 main,链接时会与主程序的 main 冲突,导致编译报错。

库编译到 .o 阶段就停止,不会进行最终链接,因此也不需要 main 函数

2 动静态库使用区别

库的制作和使用中,使用动态库是最佳实践!gcc/g++能直接形成动态库

动静态库在思想上一样,操作有所不同

动态库在编译时:

bash 复制代码
target=libmyc.so
src=$(wildcard *.c)
obj=$(src:.c=.o)
cc=gcc

$(target):$(obj)
    $(cc) -shared -o $@ $^

%.o:%.c
    $(cc) -fPIC -c $<//动态库比静态库增加的部分

-shared表示要形成动态库,不是可执行文件

编译时:你通过 -L myc/lib -lmyc 告诉了编译器库的位置,所以编译成功生成了 myexe。

运行时:程序由操作系统加载,操作系统不知道库的路径,因此报错:

bash 复制代码
./myexe: error while loading shared libraries: libmyc.so: cannot open shared object file: No such file or directory

我们需要告诉编译器路径:

bash 复制代码
gcc -o myexe main.c -I myc/include -L myc/lib -lmyc

为什么静态库没有这个问题?
静态库在编译时就被完整打包进可执行文件,运行时不再依赖外部库文件,因此不会出现 "找不到库" 的运行时错误。

动态库是运行时加载,需要操作系统能找到库文件。

编译成功 ≠ 运行成功,必须额外处理运行时库路径问题。

静态库是编译时嵌入,运行时无依赖,但可执行文件体积更大。

3 Linux 下让 OS 找到动态库的 4 种方法

动态库在运行时需要被操作系统加载,因此必须让系统能找到库文件路径。以下是 4 种常用解决方案:

🔍 系统默认查找路径

Linux 系统会默认在以下路径搜索动态库:

  • /lib64
  • /usr/lib64
  • 其他标准库路径(如 /lib/usr/lib

🛠️ 方法 1:把库安装到系统路径下

适用场景:库需要被多个程序共享,适合正式部署。

  • 操作:将 .so 文件复制到 /usr/lib64/lib64

  • 示例:

    复制代码
    sudo cp libmyc.so /usr/lib64
    sudo ldconfig  # 更新库缓存
  • 优点:永久生效,所有程序可直接使用

  • 缺点:需要 root 权限,可能污染系统库

🛠️ 方法 2:在系统路径下建立软连接

适用场景:不想移动原库文件,又想让系统找到。

  • 操作:为库文件在系统标准路径下创建符号链接

  • 示例:

    复制代码
    sudo ln -s /path/to/your/libmyc.so /usr/lib64/libmyc.so
    sudo ldconfig
  • 优点:保留原库位置,不污染系统库目录

  • 缺点:仍需要 root 权限

🛠️ 方法 3:导出环境变量 LD_LIBRARY_PATH

适用场景:临时测试、开发环境,无 root 权限。

  • 操作:临时添加库路径到环境变量

  • 示例:

    复制代码
    export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/your/lib
  • 永久化 :将命令写入 ~/.bashrc~/.bash_profile

    复制代码
    echo 'export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/your/lib' >> ~/.bashrc
    source ~/.bashrc
  • 优点:无需 root 权限,灵活可控

  • 缺点:临时方案,仅对当前终端 / 用户生效;若变量未设置则可能失效

🛠️ 方法 4:配置 /etc/ld.so.conf.d/(少见)

适用场景:需要永久生效,且不想污染系统路径,适合服务器部署。

  • 操作步骤:

    1. /etc/ld.so.conf.d/ 下创建 .conf 文件

      复制代码
      sudo vim /etc/ld.so.conf.d/myc.conf
    2. 写入库路径(一行一个路径)

      复制代码
      /path/to/your/lib
    3. 更新系统库缓存

      复制代码
      sudo ldconfig
  • 优点:永久生效,系统全局可用,结构清晰

  • 缺点:需要 root 权限

✅ 方法对比

方法 权限要求 生效范围 推荐场景
安装到系统路径 root 全局 正式部署、共享库
建立软连接 root 全局 保留原库位置的共享库
LD_LIBRARY_PATH 临时 / 用户 开发测试、无 root 权限
配置 ld.so.conf.d root 全局 永久生效、服务器部署

💡 验证方法

运行 ldd 命令查看程序依赖的库是否能被找到:

复制代码
ldd ./myexe

若输出中动态库路径显示为 => /path/to/libxxx.so,则说明配置成功。

4 三个问题

(1)c/c++为什么头文件,源文件分离?

头文件是接口,告诉编译器 "有什么";
源文件是实现,告诉计算机 "怎么做"。
分离的目的:
隐藏实现、避免冲突、提高编译速度、让代码模块化、可制作静态 / 动态库

(2)如果同时存在动静态库?

优先进行动态连接

-static会强制编译器,优先使用静态连接(强制静态链接时,系统必须存在对应的静态库文件(libxxx.a)。若仅提供动态库而无静态库,即使加了 -static,gcc 也会因找不到静态库而报错)

(3)如何理解和使用别人的库?

包含头文件(知道函数声明)
写代码调用函数(使用功能)
编译时指定 -I -L -l(让编译器找到文件)
动态库运行时要加路径,静态库运行不需要任何依赖。


五 动态库和静态库

1 动态库

动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码

  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码

  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)

  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

(1)动态库生成

bash 复制代码
// Makefile
libmystdio.so:my_stdio.o my_string.o 
 
 gcc -o $@ $^ -shared 
%.o:%.c
 gcc -fPIC -c $< 
 
.PHONY:clean 
clean:
 @rm -rf *.so *.o stdc* 
 @echo "clean ... done" 
 
.PHONY:output 
output: 
 @mkdir -p stdc/include 
 @mkdir -p stdc/lib 
 @cp -f *.h stdc/include 
 @cp -f *.so stdc/lib 
 @tar -czf stdc.tgz stdc 
 @echo "output stdc ... done"

shared:表示生成共享库格式

  • fPIC:产生位置无关码 (position independent code)

  • 库名规则:libxxx.so

(2)动态库使用

cs 复制代码
// 场景1:头⽂件和库⽂件安装到系统路径下 
$ gcc main.c -lmystdio
// 场景2:头⽂件和库⽂件和我们⾃⼰的源⽂件在同⼀个路径下 
$ gcc main.c -L. -lmymath // 从左到右搜索-L指定的⽬录 
// 场景3:头⽂件和库⽂件有⾃⼰的独⽴路径 
$ gcc main.c -I头⽂件路径 -L库⽂件路径 -lmymath
$ ldd libmystdio.so // 查看库或者可执⾏程序的依赖 
 linux-vdso.so.1 => (0x00007fffacbbf000)
 libc.so.6 => /lib64/libc.so.6 (0x00007f8917335000)
 /lib64/ld-linux-x86-64.so.2 (0x00007f8917905000)
 
// 以场景2为例 
$ ll
total 24
-rwxrwxr-x 1 whb whb 8592 Oct 29 14:50 libmystdio.so
-rw-rw-r-- 1 whb whb 359 Oct 19 16:07 main.c
-rw-rw-r-- 1 whb whb 447 Oct 29 14:50 my_stdio.h
-rw-rw-r-- 1 whb whb 447 Oct 29 14:50 my_string.h
$ gcc main.c -L. -lmystdio
$ ll
total 36
-rwxrwxr-x 1 whb whb 8600 Oct 29 14:51 a.out
-rwxrwxr-x 1 whb whb 8592 Oct 29 14:50 libmystdio.so
-rw-rw-r-- 1 whb whb 359 Oct 19 16:07 main.c
-rw-rw-r-- 1 whb whb 447 Oct 29 14:50 my_stdio.h
-rw-rw-r-- 1 whb whb 447 Oct 29 14:50 my_string.h
[whb@bite-alicloud other]$ ./a.out 
...

(3)库运行搜索路径

问题:

cpp 复制代码
$ ldd a.out 
 linux-vdso.so.1 => (0x00007fff4d396000)
 libmystdio.so => not found//说明动态库 libmystdio.so 未被系统找到,运行程序时会报加载错误
 libc.so.6 => /lib64/libc.so.6 (0x00007fa2aef30000)
 /lib64/ld-linux-x86-64.so.2 (0x00007fa2af2fe000)

解决方案:

·拷贝 .so 文件到系统共享库路径下,一般指 /usr/lib、/usr/local/lib、/lib64 或者开篇指明的库路径等

·向系统共享库路径下建立同名软连接

·更改环境变量:LD_LIBRARY_PATH

l·dconfig 方案:配置 /etc/ld.so.conf.d/,ldconfig 更新

(4)使用外部库

我们现在没接触过太多的库,唯⼀接触过的就是C、C++标准库,这⾥我们可以推荐⼀个好玩的图形库:ncurses

cpp 复制代码
// 安装 
// Centos
$ sudo yum install -y ncurses-devel
// ubuntu
$ sudo apt install -y libncurses-dev

2 静态库

静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中,程序运行的时候将不再需要静态库。

  • 一个可执行程序可能用到许多的库,这些库运行有的是静态库,有的是动态库,而我们的编译默认为动态链接库,只有在该库下找不到动态.so的时候才会采用同名静态库。我们也可以使用gcc的-static 强转设置链接静态库。

(1) 静态库生成

cpp 复制代码
// Makefile
libmystdio.a:my_stdio.o my_string.o
 @ar -rc $@ $^
 @echo "build $^ to $@ ... done"
%.o:%.c
 @gcc -c $<
 @echo "compling $< to $@ ... done"
.PHONY:clean
clean:
 @rm -rf *.a *.o stdc*
 @echo "clean ... done"
.PHONY:output
output:
 @mkdir -p stdc/include
 @mkdir -p stdc/lib
 @cp -f *.h stdc/include
 @cp -f *.a stdc/lib
 @tar -czf stdc.tgz stdc
 @echo "output stdc ... done"

ar 是 gnu 归档工具, rc 表示 (replace and create)

cpp 复制代码
$ ar -tv libmystdio.a 
rw-rw-r-- 1000/1000 2848 Oct 29 14:35 2024 my_stdio.o
rw-rw-r-- 1000/1000 1272 Oct 29 14:35 2024 my_string.o

t: 列出静态库中的文件

• v:verbose 详细信息

(2)静态库使用

cpp 复制代码
// 任意⽬录下,新建 
// main.c,引⼊库头⽂件 
1
2#include "my_stdio.h"
#include "my_string.h"
#include <stdio.h>
int main()
{
 const char *s = "abcdefg";
 printf("%s: %d\n", s, my_strlen(s));
 mFILE *fp = mfopen("./log.txt", "a");
 if(fp == NULL) return 1;
 mfwrite(s, my_strlen(s), fp);
 mfwrite(s, my_strlen(s), fp);
 mfwrite(s, my_strlen(s), fp);
 mfclose(fp);
 return 0;
}
// 场景1:头⽂件和库⽂件安装到系统路径下 
$ gcc main.c -lmystdio
// 场景2:头⽂件和库⽂件和我们⾃⼰的源⽂件在同⼀个路径下 
$ gcc main.c -L. -lmymath
// 场景3:头⽂件和库⽂件有⾃⼰的独⽴路径 
$ gcc main.c -I头⽂件路径 -L库⽂件路径 -lmymath

-L:指定库路径

-I:指定头文件搜索路径

-l:指定库名 - 测试目标文件生成后,静态库删掉,程序照样可以运行

  • 库文件名称和引入库的名称:去掉前缀 lib,去掉后缀 .so、.a,如:libc.so -> c
相关推荐
软件资深者2 小时前
iVentoy 完整使用教程:一根网线批量装系统,增强版 PXE 服务器一键部署
运维·服务器·网络·网络备份·网刻·网络安装系统
2601_949814692 小时前
ubuntu 安装 Redis
linux·redis·ubuntu
旺旺碎碎冰_2 小时前
【CVPR2026】CREval: 一个针对复杂指令创意图像生成的自动化可解释评估框架
运维·自动化
薛定谔的悦2 小时前
站控显示下级从控EMS的版本信息开发(设计多线程和TCP通讯)
linux·网络·数据库·网络协议·tcp/ip·ems
看海的四叔2 小时前
【Linux】命令行常规操作全攻略:入门+实战+速查
linux·运维·github·命令行·batch命令
skiy2 小时前
华为HuaweiCloudStack(一)介绍与架构
服务器·华为·架构
a里啊里啊2 小时前
Docker安装全流程-包括修改默认安装路径
运维·docker·容器
信创DevOps先锋2 小时前
Gitee DevOps:国产化研发体系的破局者与赋能者
运维·安全·devops
RrEeSsEeTt2 小时前
【HackTheBox】- BoardLight 靶机学习
linux·学习·网络安全·渗透测试·kali·红队·hackthebox