当前位置: 首页 > article >正文

C++生成静态库和动态库

什么是静态库和动态库

在项目开发中,或多或少地需要使用到第三方(非编译器提供)的程序库,使用第三方的程序库能够减少重复造轮子的工作,提高开发效率。本文将介绍如何把自己的写的程序制作为程序库提供给他人使用,学会制作程序库后,自然也就会掌握了如何使用他人提供的程序库了。

程序库从使用方式上分为两种,静态库和动态库。当我们在使用第三方提供的静态库时,当编译程序时,需要将我们自己写的程序和第三方库链接在一起形成一个单独的程序。而当使用第三方提供的动态库时,虽然我们使用了动态库提供的功能,但是我们只需要单独编译我们自己写的程序,当程序运行时并使用到了动态库中提供的功能时,才会把动态库中对应的程序的二进制代码加载进内存中。

静态库和动态库主要在使用上有些差别,其本质经过源程序编译、汇编后成为的二进制代码。下面的内容将在 linux 下进行实验,介绍如何制作静态库和动态库。

制作静态库

先准备实验需要的代码,如下所示,不用去关心代码细节。

.
├── include
│   └── head.h
├── main.cc
└── src
    ├── add.cc
    ├── div.cc
    ├── mult.cc
    └── sub.cc

2 directories, 6 files
// main.cc 为用于测试的代码
#include <iostream>
#include "./include/head.h"

int main()
{
    std::cout << add(1, 2) << std::endl;

    std::cout << sub(5, 2) << std::endl;

    std::cout << mult(2, 3) << std::endl;

    std::cout << divi(5, 2) << std::endl;
}

第一:将需要制作为库的源代码 *.cc 编译、汇编为二进制目标文件 ``*.o` 。

$ g++ src/*.cc -c

$ tree
.
├── add.o
├── div.o
├── include
│   └── head.h
├── main.cc
├── mult.o
├── src
│   ├── add.cc
│   ├── div.cc
│   ├── mult.cc
│   └── sub.cc
└── sub.o

第二:将生成的二进制文件打包成静态库

$ ar rcs libcalc.a *.o
# 生成名为 libcalc.a 的静态库

在linux下,静态库的命名方式为 libxxx.a,中间的 xxx 为程序库的名字。windows下,静态库的命名方式为 libxxx.lib。

这里简单介绍一下“打包”工具 ar

The GNU ar program creates, modifies, and extracts from archives. An archive is a single file holding a collection of other files in a structure that makes it possible to retrieve the original individual files (called members of the archive).

ar 程序可以把多个文件合为一个文件,然后访问合并后的文件和访问合并前的文件是一样的。
简单解释一下使用 ar 打包程序库的三个参数 r, c, s

  • r, replace existing or insert new file(s) into the archive
  • c, do not warn if the library had to be created
  • s, create an archive index (cf. ranlib)

第三步:提供静态库和头文件。将生成的静态库和对应的头文件发布出去,就可以使用该静态库了。例如在上面的例子中,将 include/head.hlibcalc.a 提供出去。

使用静态库

仍然以上面的示例为例,将测试文件、静态库及其对应的头文件放在一个目录下,便于实验测试。

$ tree
.
├── head.h
├── libcalc.a
└── main.cc
$ g++ main.cc -L ./ -lcalc -o main
$ ./main
3
3
6
2.5

-L, 指定程序库的搜索路径;-l 指定程序库的名称,需要掐头(lib)去尾(.a)。

制作动态库

仍然以上面的代码为示例。在 linux 中,动态库以 lib 为前缀,.so 为后缀,例如 libcalc.so。在 windows 中,动态库以 lib 为前缀,以 dll 为后缀,例如 libcalc.dll

第一步:将需要制作为动态库的源文件编译、汇编为二进制文件。

$ g++ src/*.cc -c -fpic

# 生成 *.o 二进制文件

-fpic 选项的作用是,生成位置无关代码(Position Independent Code,PIC)或者说是生成共享库时的代码。

(以下来自 ChatGPT,仅供参考)
PIC是一种特殊的代码,可以在内存中的任何位置执行而不受影响。它是动态链接库(shared library)的一种关键概念。当你编译一个动态链接库时,通常希望库的代码可以在内存中的不同位置加载,而不是绑定到特定的地址。这种灵活性使得同一份共享库可以被多个进程共享,因为它们可以在内存的不同位置加载,而不会相互冲突。

第二步:使用 gcc/g++ 命令生成动态库

$ g++ -shared *.o -o libcalc.so

-shared 选项用于告诉编译器生成一个动态库。

第一步和第二部也可以合并为一步,如下命令所示

$ g++ src/*.cc -shared -o libcalc.so -fpic

第三步:将制作的动态库和对应的头文件发布出去。

使用动态库

尝试使用和静态库一样的方式进行编译。

$ g++ main.cc -L ./ -lcalc -o main
$ ./main
./main: error while loading shared libraries: libcalc.so: cannot open shared object file: No such file or directory

可以看到,能成编译通过,但是运行报错,报错的内容为:找不到动态库。这是因为动态库不会和程序一起编译,上述编译编译的命令只是告诉了编译器在编译时能够找到动态库,但是当实际程序运行时,却搜索不到需要使用的动态库,因此程序报错。程序运行时,动态库的查找和内存加载操作是由 动态链接器 来完成的。

(以下来自ChatGPT)

动态链接器(Dynamic Linker)是操作系统的一部分,负责在程序运行时将程序中使用的动态库(共享库)加载到内存中,并解析程序与库之间的符号链接。

动态链接器搜索动态的路径为 /lib/usr/lib,因此解决办法之一就是将动态库拷贝到 /libusr/lib 目录下,然后就可以在运行时成功加载了。

$ cp /xxx/xxx/libcalc.so /usr/lib/
$ g++ main.cc -lcalc -o main
$ ./main
3
3
6
2.5

小结

本文介绍了静态库和动态库的区别,以及如何制作、使用静态库和动态库。但本文只是以简单的示例进行演示,对于复杂的项目,生成程序库需要需要借助 shell 脚本或 CMake 来完成一些复杂的逻辑操作。

参考资料

Linux 静态库和动态库


http://www.kler.cn/a/154999.html

相关文章:

  • C#从入门到放弃
  • STM32中,不进行printf改写通过函数达到同款效果
  • 浪潮信息“源”Embedding模型登顶MTEB榜单第一名
  • CTFHub每日练习
  • netmap.js:基于浏览器的网络发现工具
  • arcgis做buffer
  • 智慧用电安全动态监控系统
  • centos7-docker安装与使用
  • 网络虚拟化场景下网络包的发送过程
  • C/C++---------------LeetCode第35. 搜索插入位置
  • C++ day48 打家劫舍
  • 数学建模之典型相关分析
  • Redis--10--Pipeline
  • 乱序学机器学习——主成分分析法PCA
  • node.js express路由和中间件
  • c++ 写成.h .cpp main.cpp 多文件形式
  • Gradio库的安装和使用教程
  • 使用Visual Studio创建第一个C代码工程
  • 二维数组处理(一)
  • 基于windows系统使用Python对于pc当前的所有窗口的相关操作接口
  • 部署springboot项目到GKE(Google Kubernetes Engine)
  • 逻辑回归与正则化 逻辑回归、激活函数及其代价函数
  • 2024年美国大学生数学建模竞赛(MCM/ICM)论文写作方法指导
  • 基于PHP的高中生物学习平台
  • prometheus|云原生|kubernetes内部安装prometheus
  • 贝锐向日葵与华为达成合作,启动鸿蒙原生应用开发