1 编译器选项
2 C 运行时 (CRT) 和 C++ 标准库 (STL).lib 文件
Note
微软的 C++ 标准库实现通常被称为 STL 或标准模板库。虽然 C++ 标准库是 ISO 14882 定义的库的正式名称,但由于“STL”和“标准模板库”在搜索引擎中的使用较为广泛,我们偶尔也会使用这些名称,以便于查找我们的文档。
从历史角度来看,“STL”最初指的是 Alexander Stepanov 编写的标准模板库 (STL)。该库的部分内容已在 C++ 标准库中标准化。该标准库还包含 ISO C 运行时库、Boost 库的部分内容以及其他功能。有时,“STL”也指由 Stepanov 的 STL 改编而来的 C++ 标准库中的容器和算法部分。在本文档中,标准模板库 (STL) 指的是整个 C++ 标准库。
2.1 C 运行时 .lib 文件
ISO C 标准库是 C++ 标准库的一部分。实现 CRT 的 Visual C++ 库支持本机代码开发,以及混合本机代码和托管代码。所有版本的 CRT 都支持多线程开发。大多数库都支持静态链接(将库直接链接到代码中)或动态链接(允许代码使用通用 DLL 文件)。
在 Visual Studio 2015 中,CRT 被重构为新的二进制文件。通用 CRT (UCRT) 包含标准 C99 CRT 库导出的函数和全局变量。UCRT 现在是 Windows 组件,并作为 Windows 10 及更高版本的一部分提供。UCRT 的静态库、DLL 导入库和头文件现在可以在 Windows SDK 中找到。安装 Visual C++ 时,Visual Studio 安装程序会安装使用 UCRT 所需的 Windows SDK 子集。您可以在 Visual Studio 2015 及更高版本支持的任何 Windows 版本上使用 UCRT。您可以使用 vcredist 将其重新分发给 Windows 10 或更高版本以外的受支持 Windows 版本。有关更多信息,请参阅 重新分发 Visual C++ 文件。
下表列出了实现 UCRT 的库。
| 图书馆 | 关联 DLL | 特征 | 选项 | 预处理器指令 |
|---|---|---|---|---|
libucrt.lib | 没有任何 | 将 UCRT 静态链接到您的代码中。 | /MT | _MT |
libucrtd.lib | 没有任何 | 静态链接的 UCRT 调试版本。不可再分发用于。 | /MTd | _DEBUG,_MT |
ucrt.lib | ucrtbase.dll | UCRT 的 DLL 导入库。 | /MD | _MT,_DLL |
ucrtd.lib | ucrtbased.dll | UCRT 调试版本的 DLL 导入库。不可再分发。 | /MDd | _DEBUG,_MT,_DLL |
vcruntime 库包含 Visual C++ CRT 实现特定的代码:异常处理和调试支持、运行时检查和类型信息、实现细节以及某些扩展库函数。vcruntime 库版本需要与你使用的编译器版本匹配。
下表列出了实现 vcruntime 库的库。
| 图书馆 | 关联 DLL | 特征 | 选项 | 预处理器指令 |
|---|---|---|---|---|
libvcruntime.lib | 没有任何 | 静态链接到您的代码。 | /MT | _MT |
libvcruntimed.lib | 没有任何 | 使用静态链接的调试版本。不可重新分发。 | /MTd | _MT,_DEBUG |
vcruntime.lib | vcruntime<version>.dll | vcruntime 的 DLL 导入库。 | /MD | _MT,_DLL |
vcruntimed.lib | vcruntime<version>d.dll | 用于调试 vcruntime 的 DLL 导入库。不可再分发。 | /MDd | _DEBUG,_MT,_DLL |
Note
重构 UCRT 时,并发运行时函数被移至
concrt140.dll,后者已添加到 C++ 可再发行组件包中。此 DLL 是 C++ 并行容器和算法(例如concurrency::parallel_for)所必需的。此外,由于 Windows XP 没有条件变量,C++ 标准库需要 Windows XP 上的此 DLL 来支持同步基元。
初始化 CRT 的代码位于多个库之一中,具体取决于 CRT 库是静态链接、动态链接,还是本机代码、托管代码或混合代码。此代码处理 CRT 启动、内部每线程数据初始化和终止。它特定于所使用的编译器版本。此库始终是静态链接的,即使使用动态链接的 UCRT 也是如此。
下表列出了实现 CRT 初始化和终止的库。
| 图书馆 | 特征 | 选项 | 预处理器指令 |
|---|---|---|---|
libcmt.lib | 将本机 CRT 启动静态链接到您的代码中。 | /MT | _MT |
libcmtd.lib | 静态链接本机 CRT 启动的调试版本。不可再分发。 | /MTd | _DEBUG,_MT |
msvcrt.lib | 用于本机 CRT 启动的静态库,可与 DLL UCRT 和 vcruntime 一起使用。 | /MD | _MT,_DLL |
msvcrtd.lib | 本机 CRT 启动的调试版本的静态库,可与 DLL UCRT 和 vcruntime 一起使用。不可再分发使用。 | /MDd | _DEBUG,_MT,_DLL |
msvcmrt.lib | 用于本机和托管 CRT 启动混合的静态库,可与 DLL UCRT 和 vcruntime 一起使用。 | /clr | |
msvcmrtd.lib | 用于本机和托管 CRT 启动混合的调试版本的静态库,可与 DLL UCRT 和 vcruntime 一起使用。不可再分发。 | /clr | |
msvcurt.lib | 纯托管 CRT 的静态库已废弃。 | /clr:pure | |
msvcurtd.lib | 纯托管 CRT 调试版本的已废弃使用静态库。不可重新分配。 | /clr:pure |
如果您从命令行链接程序时没有使用指定 C 运行时库的编译器选项,则链接器将使用静态链接的 CRT 库:libcmt.lib、libvcruntime.lib 和 libucrt.lib。
使用静态链接的 CRT 意味着 C 运行时库保存的任何状态信息都将是该 CRT 实例的本地信息。例如,如果您在使用静态链接的 CRT 时使用 strtok ,则 strtok 解析器的位置与链接到另一个静态 CRT 实例的同一进程(但在不同的 DLL 或 EXE 中)中的代码中使用 strtok 的状态无关。相反,动态链接的 CRT 共享动态链接到 CRT 的进程内所有代码的状态。如果您使用这些函数的新的更安全的版本,则不存在此问题;例如,strtok_s 不存在此问题。
由于通过链接到静态 CRT 构建的 DLL 具有其自己的 CRT 状态,因此,除非了解并期望其后果,否则我们不建议你静态链接到 DLL 中的 CRT。例如,如果你调用 _set_se_translator 一个可执行文件,而该可执行文件加载了链接到其自己的静态 CRT 的 DLL,则转换器将无法捕获 DLL 中的代码生成的任何硬件异常,但可以捕获主可执行文件中的代码生成的硬件异常。
如果您使用 /clr 编译器开关,您的代码将与静态库 msvcmrt.lib 链接。静态库在托管代码和本机 CRT 之间提供代理。您不能将静态链接的 CRT(/MT 或 /MTd 选项)与 /clr 一起使用。请改用动态链接库(/MD 或 /MDd)。纯托管 CRT 库在 Visual Studio 2015 中已弃用,并且在 Visual Studio 2017 中不受支持。
有关使用 CRT 的更多信息 /clr,请参阅 混合(本机和托管)程序集。
要构建应用程序的调试版本,_DEBUG 必须定义该标志,并且必须将应用程序链接到其中一个库的调试版本。有关使用库文件的调试版本的更多信息,请参阅 CRT 调试技术。
此版本的 CRT 不完全符合 C99 标准。在 Visual Studio 2019 16.8 版之前的版本中,<tgmath.h> 不支持标头。所有版本均不支持 CX_LIMITED_RANGE 和 FP_CONTRACT 编译指示宏。某些元素(例如标准 IO 函数中参数说明符的含义)默认使用旧式解释。您可以使用**/Zc**编译器一致性选项并指定链接器选项来控制库一致性的某些方面。
2.2 C++ 标准库 (STL).lib 文件
| C++ 标准库 | 特征 | 选项 | 预处理器指令 |
|---|---|---|---|
libcpmt.lib | 多线程、静态链接 | /MT | _MT |
msvcprt.lib | 多线程、动态链接(msvcp<version>.dll 的导入库) | /MD | _MT,_DLL |
libcpmtd.lib | 多线程、静态链接 | /MTd | _DEBUG,_MT |
msvcprtd.lib | 多线程、动态链接(msvcp<version>d.dll 的导入库) | /MDd | _DEBUG,_MT,_DLL |
构建项目的发布版本时,会默认链接一个基本 C 运行时库(libcmt.lib、msvcmrt.lib、 msvcrt.lib),具体取决于你选择的编译器选项(多线程、DLL、 /clr)。如果你在代码中包含一个 C++ 标准库头文件,Visual C++ 会在编译时自动链接一个 C++ 标准库。例如:
#include <ios>为了实现二进制兼容性,一个导入库可以指定多个 DLL 文件。版本更新可能会引入 点库 ,即引入新库功能的独立 DLL。例如,Visual Studio 2017 15.6 版引入了 msvcp140_1.dll 更多标准库功能,且不会破坏所支持的应用程序二进制接口 (ABI) msvcp140.dll。Visual msvcprt.libStudio 2017 15.6 版工具集中包含的导入库支持这两个 DLL,此版本的 vcredist 会安装这两个 DLL。点库一旦发布便具有固定的 ABI,并且永远不会依赖于更高版本的点库。
2.3 ⭐ 如果应用程序使用多个 CRT 版本,会出现什么问题?
每个可执行映像(EXE 或 DLL)都可以拥有自己的静态链接 CRT,也可以动态链接到 CRT。特定映像静态包含或动态加载的 CRT 版本取决于构建该映像所使用的工具和库的版本。单个进程可以加载多个 EXE 和 DLL 映像,每个映像都有自己的 CRT。每个 CRT 可能使用不同的分配器,具有不同的内部结构布局,并且使用不同的存储安排。这意味着分配的内存、CRT 资源或跨 DLL 边界传递的类可能会导致内存管理、内部静态使用或布局解释方面的问题。例如,如果一个类在一个 DLL 中分配,但传递给另一个 DLL 并被另一个 DLL 删除,那么应该使用哪个 CRT 释放器?由此造成的错误可能很小,也可能立即致命,因此不建议直接传输此类资源。
您可以使用应用程序二进制接口 (ABI) 技术来避免许多此类问题,因为它们的设计目标是稳定且可版本控制。设计您的 DLL 导出接口以按值传递信息,或处理由调用方传入的内存,而不是在本地分配并返回给调用方的内存。使用封送处理技术在可执行映像之间复制结构化数据。在本地封装资源,并且仅允许通过向客户端公开的句柄或函数进行操作。
如果进程中的所有映像都使用相同的动态加载版本的 CRT,也可以避免其中一些问题。为了确保所有组件都使用相同的 DLL 版本的 CRT,请使用选项 /MD 构建它们,并使用相同的编译器工具集和属性设置。
如果您的程序跨 DLL 边界传递某些 CRT 资源,请务必小心。即使使用相同版本的 CRT,文件句柄、区域设置和环境变量等资源也可能导致问题。有关相关问题及其解决方法的更多信息,请参阅 跨 DLL 边界传递 CRT 对象时可能出现的错误。
3 FAQ
3.1 msvc 中有的库是不可再分发的,是什么意思
Faq
在 MSVC(Microsoft Visual C++)中,“有的库是不可再分发的”的意思是:
这些库文件只能在开发/编译阶段使用,不能被你打包、复制或随你的应用程序一起分发给其他用户或部署到其他设备上。
一、什么叫“再分发”?
再分发(redistribution)是指你在发布自己的程序时,把它依赖的运行时库(DLL 或 LIB 文件)一并打包给用户的行为。
常见合法的再分发形式:
- 安装包中附带 DLL(如
vcruntime140.dll)- 发布静态链接版本(如使用
/MT编译选项)- 引导用户安装 Visual C++ Redistributable(官方安装器)
二、哪些库是不可再分发的?
MSVC 中有些库属于仅供开发使用的库,不允许你复制或打包进自己的程序中,比如:
类型 举例 是否可再分发 说明 调试版 CRT 库 MSVCRTD.lib,ucrtd.dll(debug)❌ 只能用于调试和开发,不能用于生产环境或发布给用户 一些内部工具库 vcpkg临时构建的 DLL、MS internal tools❌ 仅供编译/测试阶段使用 MSVC 编译工具本身 cl.exe,link.exe,c1xx.dll❌ 属于工具链组件,受 EULA 限制 ✅ 可再分发的库一般包括:
Release 模式下的运行库 DLL,例如:
vcruntime140.dllmsvcp140.dllconcrt140.dll这些库来自微软的官方 VC++ Redistributable 安装包
三、如何判断某个库是否可以再分发?
查阅 Microsoft 官方 Redistributable List
通过库路径判断
如果库文件在如下路径中,一般是“不可再分发”:
C:\Program Files (x86)\Microsoft Visual Studio\2022\BuildTools\VC\Tools\MSVC\<version>\debug\...查看文件是否带有
_d后缀
- 如
msvcp140d.dll→ 调试版,不可再分发- 没有
d→ 正式版,可再分发查阅 EULA(许可协议)
四、违反再分发限制的后果
- 违反微软许可协议(Microsoft Software License Terms)
- 法律风险(尤其在商业软件中)
- 程序在其他机器上无法运行(因为缺失合法的运行库)
五、安全做法建议
目标 推荐做法 打包运行库 使用微软的 VC++ Redistributable 避免发布调试库 构建发布版(Release),并避免链接 _d版本的 DLL静态链接运行库 使用 /MT或/MTd(需注意 LGPL 等开源库兼容性)使用 vcpkg 等工具 只发布 Release 构建产物,排除 vcpkg 的 debug 库