从零开始:Linux环境下如何制作静态库与动态库

个人主页:chian-ocean

文章专栏-Linux

前言

动静态库是编程中两种主要的库类型,它们用于帮助开发者复用已有的代码,而不需要每次都从头开始编写。它们的主要区别在于链接和加载的时机、方式以及使用场景

就是一些已经写好并且经过测试的功能模块,你只需要在自己的程序中"借用"这些功能,而无需重新做同样的工作。这样不仅节省了时间,还能提高代码的质量和稳定性。

库的形式

库的形式 主要有两种,分别是 静态库动态库。它们的区别在于程序如何使用这些库,以及库文件在程序中的存储方式。

静态库 (Static Library)

静态库是在程序编译时就被链接到可执行文件中的库,所有的库代码会被嵌入到最终生成的程序中。程序在运行时不再依赖外部的库文件。

文件扩展名:
  • Linux/Unix 系统.a(archive)
  • Windows 系统.lib

动态库 (Dynamic Library)

动态库是在程序运行时 加载的库,程序并不会将库的代码嵌入到程序中,而是在程序启动时,或者在程序运行时动态加载需要的库文件。

文件扩展名:
  • Linux/Unix 系统.so(Shared Object)
  • Windows 系统.dll(Dynamic Link Library)

总结:

特性 静态库 动态库
链接时机 编译时链接,库代码被嵌入到程序中 运行时链接,程序在执行时动态加载库
可执行文件大小 程序文件较大,因为库代码被包含在其中 程序文件较小,只包含对库的引用,库的代码在外部
依赖关系 不依赖外部库文件,所有代码在可执行文件中 依赖外部库文件,程序必须确保运行时找到对应的库文件
共享 每个程序都包含自己需要的库代码,不共享 多个程序可以共享同一个库文件,节省内存和磁盘空间
更新 库文件更新时需要重新编译程序 库文件更新时,无需重新编译程序,只需要确保兼容性
性能 程序启动较快,因为所有代码都已经包含在内 程序启动时需要加载库文件,可能稍慢,且可能会有额外的内存管理开销

静态库

静态库的制作

**静态库(Static Library)**是一个打包了多个目标文件(.o 文件)或其他库文件的归档文件。它可以在编译时链接到程序中,生成最终的可执行文件。静态库的扩展名通常为 .a(在 Unix/Linux 系统上)。

编写源文件 : 创建包含函数实现的 .c 文件,例如 math.c

  • 简单的加减乘除。
cpp 复制代码
#include "mymath.h"                                                                 
                                                                       
int errno = 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)                                                    
    {                                                             
        errno = -1;                                               
        return -1;                                                
    }                                                             
    return x / y;                                                 
}

编写头文件: 创建包含函数实现的 .c 函数声明

cpp 复制代码
#pragma once    
#include <stdio.h>  
extern int errno;    
                     
int add(int x,int y);    
int sub(int x,int y);    
int mul(int x,int y);    
int div(int x,int y); 

编写Makefile:创建自动化构建文件

makefile 复制代码
# 定义静态库的名称
lib=libmath.a    

# 生成静态库 libmath.a 的规则
# 依赖 mymath.o 文件,并将其打包成静态库
$(lib): mymath.o  
    # 使用 ar 命令将目标文件 mymath.o 添加到静态库 libmath.a 中
    ar -rc $@ $^

# 编译 mymath.c 文件为 mymath.o 目标文件的规则
mymath.o: mymath.c   
    # 使用 gcc 编译 mymath.c 文件为目标文件 mymath.o
    gcc -c $^

# 定义伪目标 clean,它用于清理构建产生的文件
.PHONY: clean  
clean:    
    # 删除所有目标文件 (.o) 和库文件 (lib*)
    rm -rf *.o lib*    

# 定义伪目标 output,它用于创建输出目录并复制必要的文件
.PHONY: output    
output:    
    # 创建 lib/include 目录
    mkdir -p lib/include    
    # 创建 lib/libmymath 目录
    mkdir -p lib/libmymath    
    # 将所有头文件复制到 lib/include 目录
    cp *.h lib/include    
    # 将静态库 (.a 文件) 复制到 lib/libmymath 目录
    cp *.a lib/libmymath    

静态库的使用

cpp 复制代码
#include "mymath.h"  // 包含自定义的头文件 mymath.h

int main() {
    printf("add(1 + 1) = %d\n", add(1, 1));

    printf("sub(1 + 1) = %d\n", sub(1, 1));

    printf("mul(1 + 1) = %d\n", mul(1, 1));

    printf("div(1 + 1) = %d\n", div(1, 1));
    return 0; 
}

写一个代码利用自己写的库;

  • 此时的静态库在/lib/libmymath的路径下。
  • 头文件在/lib/include的路径下。
  • 可执行程序在/mian的目录下。

库路径和头文件路径只有用户知道

bash 复制代码
gcc main.c -I ../lib/include/ -L ../lib/libmymath/ -lmymath
  • gcc main.c :使用 gcc 编译器来编译 main.c 文件。

  • -I ./lib/include/ :这个选项告诉编译器在 ./lib/include/ 目录中查找头文件 (*.h)。-I 后面跟着的是头文件所在的目录。

  • -L ./lib/libmymath/ :这个选项告诉编译器在 ./lib/libmymath/ 目录中查找库文件 (*.a*.so),-L 后面跟的是库文件所在的目录。

  • -lmymath :这个选项告诉编译器链接 libmymath 库。编译器会自动查找并链接名为 libmymath.alibmymath.so 的库文件。注意,lib 前缀和 .a 后缀会被自动省略。

库路径和头文件路径在系统路径下

bash 复制代码
/usr/lib64 #库的系统默认路径
bash 复制代码
/usr/include #头文件的系统默认路径

这时候我们仅仅需要手动连接库就好了。

bash 复制代码
gcc main.c  -lmymath

动态库

动态库的制作

  • 把静态库的的方法做成动态库。

编写Makefile:创建自动化构建文件

makefile 复制代码
# 定义变量dy_mymath,表示生成的动态库文件名
dy_mymath = libmymath.so

# 生成动态库 libmymath.so 目标
# 该目标依赖于 mymath.o 文件
$(dy_mymath): mymath.o
    # 使用gcc命令生成动态库,-shared 表示生成共享库,-o 用于指定输出文件
    # $@ 代表目标文件(这里是 libmymath.so),$^ 代表所有依赖文件(这里是 mymath.o)
    gcc -shared -o $@ $^

# 生成目标文件 mymath.o
# 该目标依赖于 mymath.c 文件
mymath.o: mymath.c
    # 使用gcc编译源文件 mymath.c 生成目标文件 mymath.o
    # -fPIC 生成位置无关代码(适用于动态库),-c 只编译源文件,不进行链接
    # $^ 代表依赖文件(这里是 mymath.c)
    gcc -fPIC -c $^

# 声明 clean 为伪目标,表示 make clean 是一个命令,而不是文件
.PHONY: clean

# clean 目标,用于清理生成的目标文件和动态库文件
clean:
    # 删除所有目标文件 (.o) 和共享库文件 (.so)
    # -rf 强制删除,不询问确认
    rm -rf *.o *.so

动态库的使用

库路径和头文件路径只有用户知道

sh 复制代码
gcc main.c -I ../ -L ../ -lmymath
  • -I../ :此选项指定了头文件路径,告诉编译器去 ../ 目录下查找头文件。

  • -L../ :此选项指定了库文件路径,告诉链接器去 ../ 目录下查找库文件。

  • -lmymath :告诉编译器链接 libmymath.so 动态库。链接器会自动在路径中查找名为 libmymath.so 的动态库文件。

库路径和头文件路径在系统路径下

bash 复制代码
/usr/lib64 #库的系统默认路径
bash 复制代码
/usr/include #头文件的系统默认路径

这时候我们仅仅需要手动连接库就好了。

bash 复制代码
gcc main.c  -lmymath

执行可执行程序的时候就会报错

vb 复制代码
./a.out: error while loading shared libraries: libmymath.so: cannot open shared object file: No such file or directory

这个错误表明 动态库 libmymath.so 无法被加载,通常是因为系统在运行时无法找到该动态库。

动态库的加载

  1. 将动态库加载到lib64系统路径下,拷贝到系统默认搜索的路径的下面

  2. 软链接到lib64的系统路径下。

bash 复制代码
sudo ln -s /home/ocean/linux/file/lib_dyn/libmymath.so /lib64

将其软连接到系统路径下。

  1. 运行程序时指定动态库路径: 如果运行时无法找到动态库,使用 LD_LIBRARY_PATH 来指定动态库的位置(临时)。
bash 复制代码
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/ocean/linux/file/lib_dyn
  1. /etc/ld.so.conof.d建立自己的动态库的路径配置文件,然后执行ldconfig进行刷新。
  • 同样也可以进行动态库的加载。
相关推荐
m0_7482515215 分钟前
httpslocalhostindex 配置的nginx,一刷新就报404了
运维·nginx
黎明晓月35 分钟前
CentOS 7.9 上安装 Docker Compose
linux·docker·centos
嘿·嘘39 分钟前
第五章 STM32 环形缓冲区
linux·服务器·网络
亚林瓜子43 分钟前
Minio安装(Docker Compose方式)
运维·docker·容器·minio·compose
張萠飛1 小时前
Docker的常用镜像
运维·docker·容器
Craaaayon1 小时前
Docker基础-自定义镜像与容器网络
java·运维·网络·数据库·后端·docker·容器
weixin_307779131 小时前
Python使用SFTP批量上传和下载一个目录下的所有文件
服务器·开发语言·python
Nuttx_Fan_now1 小时前
一篇文章讲解清楚ARM9芯片启动流程
linux·mpu·u-boot·bootloader·arm9
南棱笑笑生1 小时前
20250304在飞凌OK3588-C的linux R4下提高温度控制阈值为95度
java·linux·算法
宇宙核1 小时前
Nginx或Tengine服务器配置SSL证书
服务器·nginx·ssl