静态库创建
0 前言
最近工作需要将源码中的算法剥离出来,做成静态库,防止其他组偷我们的AI算法源码。所以开始研究相关的工作
1 CRT
首先引入一个概念CRT,CRT = C runtime Library, 也就是C运行时库
CRT是你的 C++ 程序运行时所依赖的一套基础设施。
C运行时库提供了很多C/C++的基础代码,比如:
- 程序启动、调用 main()、程序退出
- malloc/free、部分 new/delete 运行时支持
- printf/scanf、fopen/fclose
- 字符串、数学、时间、区域设置
- 异常处理、RTTI、运行时检查
- Debug 模式下的堆检查和内存泄漏检测程序启动、调用 main()、程序退出
现代 MSVC 的运行库实际拆分为 UCRT、VCRuntime 和 C++ 标准库等部分
CRT的链接方式
| 参数 | CRT 链接方式 | 版本 | 典型用途 |
|---|---|---|---|
| /MT | 静态链接 CRT | Release | 独立程序、希望减少对 VC Runtime DLL 的部署依赖 |
| /MTd | 静态链接 Debug CRT | Debug | /MT 项目的本地调试 |
| /MD | 动态链接 CRT DLL | Release | EXE + DLL、插件、组件化项目 |
| /MDd | 动态链接 Debug CRT DLL | Debug | /MD 项目的本地调试 |
这是 MSVC 对四个选项的正式定义:/MT、/MTd 使用静态运行库,/MD、/MDd 使用 DLL 版本的运行库;后缀 d 表示 Debug 版本
/MT = 静态 + Release
/MTd = 静态 + Debug
/MD = 动态 + Release
/MDd = 动态 + Debug
/MT:每个模块自带一份 CRT
使用 /MT 时,CRT 的代码会静态链接进最终的 EXE 或 DLL:
App.exe
├── 业务代码
├── 第三方代码
└── 自己的一份 CRT
假设一个进程包含:
App.exe /MT
PluginA.dll /MT
PluginB.dll /MT
那么它们通常各自带有一份独立的 CRT:
App.exe → CRT #1
PluginA.dll → CRT #2
PluginB.dll → CRT #3
这些 CRT 实例拥有各自的运行时状态。微软文档明确指出,静态 CRT 的状态对于每个 EXE/DLL 实例是独立的,因此微软不建议在 DLL 中随意静态链接 CRT,除非开发者清楚了解后果。
/MT 的优点
- 减少对 Visual C++ Runtime DLL 的部署依赖
- 单个 EXE 更容易复制到其他机器
- 不容易因为目标机器缺少特定 VC Runtime DLL 而启动失败
- 适合边界清晰、相对独立的命令行工具或桌面程序
/MT 的缺点 - 每个 EXE/DLL 都可能包含一份运行库代码
- 文件通常会更大
- 不同模块可能拥有不同的 CRT 状态和内存管理环境
- 跨 DLL 传递 CRT 对象或释放内存时容易出问题
因此 App.exe 和 Plugin.dll 各自有一份,不会在运行时共同使用同一份静态库。不过,并不是整个 .lib 都会被无脑复制进去。一个 .cpp 通常生成一个 .obj;静态库默认先按 .obj 取用,再视优化设置按函数进一步裁剪
/MD:多个模块使用动态 CRT
使用 /MD 时,程序通过 DLL 使用运行库:
App.exe ─┐
PluginA.dll ─┼─→ UCRT / VCRuntime / MSVC C++ Runtime DLL
PluginB.dll ─┘
这通常更适合:
主程序 EXE
+
多个业务 DLL
+
插件 DLL
+
第三方组件
当所有模块使用兼容的 /MD 配置时,它们可以使用同一套动态加载的 CRT。微软也建议:需要让进程中的多个组件使用相同 CRT DLL 时,应统一使用 /MD,并保持工具集和相关属性一致。
/MD 的优点
- 各模块不需要重复静态包含完整运行库
- 通常可以减小各个二进制文件的体积
- 更适合 EXE、DLL、插件组成的组件化系统
- 对跨模块 CRT 状态和内存管理相对友好
/MD 的缺点 - 目标机器需要具备对应的 Visual C++ Runtime 组件
- 部署时需要安装或携带相应的 VC Redistributable
- 运行库版本或部署配置错误时,程序可能无法启动

浙公网安备 33010602011771号