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

C++动态库和静态库的生成和使用

DLL的生成

头文件

#ifndef TEST_H
#define TEST_H#include<iostream>
#include<stdexcept> 
using namespace std;#ifndef TESTAPI
#define TESTAPI __declspec(dllexport)
#else
#define TESTAPI __declspec(dllimport)
#endif extern "C" {TESTAPI int add(int a, int b);TESTAPI int sub(int a, int b);TESTAPI int mul(int a, int b);TESTAPI int div_(int a, int b);
}#endif // ! TEST_H

为什么用TESTAPI宏呢,便于后续使用接口

可以看到此时dllimport是灰色的,导出接口dllexport生效

源文件

#include"test.h"int add(int a, int b) {return a + b;
}int sub(int a, int b) {return a - b;
}int mul(int a, int b) {return a * b;
}int div_(int a, int b) {try {if (b == 0) {throw std::runtime_error("Division by zero error!"); // 抛出异常}return a / b;}catch (const std::runtime_error& e) {cout << "Error: " << e.what() << endl; // 捕获并处理异常return 0; // 或根据需求返回其他值}
}

配置类型DLL

生成DLL

DLL的调用法一

把生成的test1.dll放在测试工程test_useDll的目录下

调用DLL中的接口

#include <iostream>
#include <windows.h>  
using namespace std;// 声明函数指针
typedef int(*AddFunc)(int, int);
typedef int(*SubFunc)(int, int);
typedef int(*MulFunc)(int, int);
typedef int(*DivFunc)(int, int);int main() {// 加载DLLHINSTANCE hDLL = LoadLibraryA("test1.dll");if (!hDLL) {cout << "Failed to load DLL." << endl;return 1;}// 获取函数地址AddFunc add = (AddFunc)GetProcAddress(hDLL, "add");SubFunc sub = (SubFunc)GetProcAddress(hDLL, "sub");MulFunc mul = (MulFunc)GetProcAddress(hDLL, "mul");DivFunc div_ = (DivFunc)GetProcAddress(hDLL, "div_");if (!add || !sub || !mul || !div_) {cout << "Failed to get function address." << endl;FreeLibrary(hDLL);return 1;}// 调用DLL中的函数int a = 10, b = 5;cout << "add(a, b) = " << add(a, b) << endl;cout << "sub(a, b) = " << sub(a, b) << endl;cout << "mul(a, b) = " << mul(a, b) << endl;cout << "div_(a, b) = " << div_(a, b) << endl;// 释放DLLFreeLibrary(hDLL);return 0;
}

输出结果

DLL的调用法二

  1. 新建文件夹:E:\Desktop\test_DLL
  2. 在test_DLL下面新建include文件夹
  3. 在test_DLL下面新建lib文件夹

生成的test1.lib放到lib文件夹

test.h放到include文件夹

测试工程配置

配置宏

可以看到此时dllexport是灰色的,dllimport生效

包含目录配置

库目录配置

链接器配置

将DLL与exe放置在同一目录

实际项目中所生成的DLL如果依赖了别的DLL,需要把依赖的DLL也放在同一目录下。

正常输出

静态库lib的生成

头文件改动

  1. 头文件用条件编译,源文件不动
  2. 在生成 DLL 时定义 BUILD_DLL,在使用 DLL 时定义 USE_DLL,生成静态库时都不定义。
#ifndef TEST_H
#define TEST_H#include<iostream>
#include<stdexcept> 
using namespace std;#ifdef BUILD_DLL
#define TESTAPI __declspec(dllexport)
#elif defined(USE_DLL)
#define TESTAPI __declspec(dllimport)
#else
#define TESTAPI // 静态库时为空
#endifextern "C" {TESTAPI int add(int a, int b);TESTAPI int sub(int a, int b);TESTAPI int mul(int a, int b);TESTAPI int div_(int a, int b);
}#endif // ! TEST_H

工程导出配置

生成静态库lib

静态库lib的调用

  1. 新建文件夹:E:\Desktop\test_lib
  2. 在test_lib下面新建include文件夹,放test.h
  3. 在test_lib下面新建lib文件夹,可以看到这个lib和之前生成DLL时的lib大小不一样,其实DLL生成时一起生成的lib是动态库的导入库,不是真正的静态库。

包含目录配置

库目录配置

链接器配置

正常输出

补充知识

VS生成动态库的时候会生成一个lib,这不是静态库,和单独生成的静态库lib大小差很多 。

区别说明:

类型后缀作用说明
动态库.dll包含实际的可执行代码在运行时被加载使用
导入库.lib
(与DLL配套)
用于链接时解析符号编译器链接用,运行时还需要 .dll
静态库.lib
(独立生成)
包含实际实现代码链接时被合并到可执行文件中,运行时无需 .dll

为什么大小差很多?

导入库 .lib 只是一个轻量级的“符号索引文件”,里面只包含函数/类等符号的信息,不包含实际实现代码,因此比静态库小得多。而静态库 .lib 是将编译后的对象代码封装在里面,通常会大很多。

动态库的动态加载和静态加载

上面的DLL的调用法二就是静态加载,法一就是动态加载

特性静态加载(使用导入库)动态加载(使用 LoadLibrary)
是否需要 .lib是(导入库)
是否需要 .dll
加载时机程序启动时加载 DLL程序运行时手动加载 DLL
函数使用方式像普通函数直接用必须用 GetProcAddress
获取函数指针
错误检查编译/链接时能检查符号是否存在运行时出错才知道
是否灵活不灵活(固定依赖)很灵活(可以按需加载)

动态库和静态库的区别

项目静态库(Static Library)动态库(Dynamic Library)
文件扩展名.lib
(Windows) .a
(Linux)
.dll
(Windows) .so
(Linux)
链接方式编译时合并到 EXE 或 DLL 中程序运行时加载
是否独立可独立运行,无需库文件必须依赖 DLL 文件存在
占用体积程序体积较大(复制代码)程序体积较小(共享代码)
更新方式更新需要重新编译主程序可单独更新 DLL
调用方式直接调用函数通过导入库或动态加载调用
多程序共享不行(每个程序各自有一份)可以多个程序共享一个 DLL
运行效率启动快,运行略快(无函数指针开销)启动略慢,运行略慢(调用时需跳转)

动态库和静态库的使用场景

静态库适用场景(Static Library)

🛠 1. 简单项目 / 内部工具

  • 不想处理运行时依赖(如 .dll 丢失)
  • 工具或命令行程序,小巧独立

📦 2. 单文件部署要求

  • 例如嵌入式开发、单机部署,不能有外部依赖

🔐 3. 源码闭合封装

  • 不想暴露库的实现(不易逆向)
  • 所有代码编译进可执行文件

⚡ 4. 性能优先

  • 启动更快、调用无跳转(微小优化)
  • 无运行时装载开销

✅ 动态库适用场景(Dynamic Library)

🧩 1. 插件/模块系统

  • 比如浏览器插件、游戏引擎模块
  • 支持运行时加载和替换模块

🔁 2. 多程序共享库

  • 一个 DLL 被多个程序调用,节省内存和磁盘空间
  • 例如操作系统 API、数据库驱动等

🚀 3. 支持热更新

  • 只更换 DLL 就能修复 bug 或添加功能,无需重新编译主程序

🧪 4. 版本兼容 / 动态扩展

  • 支持不同 DLL 版本(如数据库接口、第三方 SDK)
  • 加载成功与否可动态判断,适合做可选功能

💻 5. 第三方库分发

  • 如 OpenCV、SQLite、Qt 提供 DLL 供调用
  • 避免源代码或静态库暴露

✅ 实战建议

项目类型建议使用
命令行工具、小工具静态库,部署简单
插件架构(如浏览器、IDE)动态库,可按需加载
跨多个产品共享模块动态库,节省资源
面向外部发布的 C++ SDK两者都提供(常见做法)
嵌入式系统静态库,避免外部依赖

http://www.xdnf.cn/news/4221.html

相关文章:

  • linux crash工具详解
  • android-ndk开发(1): 搭建环境
  • 星途-(4)
  • 关于Python:9. 深入理解Python运行机制
  • DeepSeek技术发展详细时间轴与技术核心解析
  • ARM子程序调用与返回
  • vscode运行python的快捷键
  • VirtualBox调整虚拟机内存和CPU
  • 信息系统项目管理师-软考高级(软考高项)​​​​​​​​​​​2025最新(八)
  • 智能体四项关键技术:MCP、A2A、ANP与函数调用的深度解析
  • 判断字符是否唯一 --- 位运算
  • 《冰雪三职业》:战士玩法攻略!
  • 精益数据分析(39/126):SaaS与移动应用商业模式的关键要点剖析
  • P6822 [PA 2012 Finals] Tax 题解
  • 【项目】基于ArkTS的网吧会员应用开发(2)
  • Qt天气预报系统更新UI界面
  • ansible基础-优化
  • 代码随想录算法训练营day9:字符串part02
  • 英伟达开源英语自动语音识别模型:nvidia/parakeet-tdt-0.6b-v2
  • android zxing QrCode 库集成转竖屏适配问题
  • 餐具瓷器品牌十大排名
  • Linux安装RTL8215网卡驱动
  • FreeRTOS系统CPU使用率统计
  • AutoGPT
  • GESP2024年3月认证C++八级( 第二部分判断题(6-10))
  • 柯西乘积定理(Cauchy Product Theorem)
  • C# 反射
  • [特殊字符] 大模型(LLMs)RAG 版面分析——文本分块面
  • 农经权二轮延包软件—摸底申请表生成
  • 数据库的并发控制