【问题标题】:CRT library typeCRT 库类型
【发布时间】:2015-04-06 06:17:00
【问题描述】:

我试图更好地掌握 Visual Studio 2013 中的 CRT 库选项(C++ -> 代码生成 -> 运行时库)以及如何知道选择哪个选项(以及何时更改默认值)。

来自MSDN

可重用库及其所有用户应使用相同的 CRT 库类型,因此应使用相同的编译器开关。

所以,我的理解是,如果您要与第三方库链接,您应该使用与构建库相同的 CRT 版本。构建库的人应指定构建中使用的 CRT 选项。

有没有办法通过查看 .lib 文件来确定使用了哪个 CRT 版本?

更重要的是,如果您不与任何第三方库链接,您将如何决定使用哪个选项?您何时会考虑更改默认设置?

【问题讨论】:

  • you should use the same CRT version that was used to build the library 不一定 - 取决于该库的界面设计得如何。如果它的设计方式是 CRT 资源永远不会跨模块边界共享,那么两个模块是否使用不同的 CRT 风格都没关系。例如,库永远不会分配调用者稍后会释放的内存。设计一个可供任何调用者使用的库确实需要一定的纪律。

标签: c++ visual-studio-2013 crt


【解决方案1】:

简答

所以,我的理解是,如果您与第三方链接 库,您应该使用与构建相同的 CRT 版本 图书馆。构建库的人应该指定什么 CRT 选项 在构建中使用。

这是最不容易出错的选项。可以混合运行时,但这样做可能会遇到意想不到的错误。

有没有办法通过查看来确定使用的是哪个 CRT 版本 在 .lib 文件中?

我不知道 .lib 本身,但如果第三方代码有 DLL 或 EXE,您可以使用 Windows Dependency Walker 工具查看它依赖于哪些 CRT DLL。

如果它是一个静态库,并且您的代码的 CRT 选择不匹配,您会在构建时看到警告。

更重要的是,如果您决定使用哪个选项 没有与任何第三方库链接?什么时候考虑 更改默认值?

对于最简单的部署,静态链接是最好的;您可以单独发布可执行文件,它就会运行。对于较大的项目,您有多个 EXE 和 DLL,如果您使用静态链接,您的代码会更大。如果您在同一个进程中有多个模块(EXE 加上 1 个或多个您自己的 DLL),最好共享相同的 CRT 代码。

更多细节(基于我之前写的博文):


库变体

您可以根据四种 C/C++ 运行时库变体来构建代码:

  • 多线程调试 DLL
  • 多线程 DLL
  • 多线程调试
  • 多线程

您可以通过右键单击 Visual Studio 中的项目并选择属性,然后单击 C/C++ 下的 代码生成 选项来选择要使用的库弹出的对话框,然后转到 Runtime Library 属性。

请记住,此设置是针对每个配置的,因为您需要为 Debug 配置选择 Debug 运行时库,然后 Release > 发布配置的运行时库。

有什么区别?

DLL 运行时库选项意味着您动态链接 C/C++ 运行时,并且为了让您的程序运行,该 DLL 需要位于您的程序的某个位置可以找到它(稍后会详细介绍)。

未提及 DLL 的选项(多线程调试多线程发布)会导致您的程序与运行时静态链接。这意味着您不需要外部 DLL 来运行程序,但是由于额外的代码,您的程序会更大,并且您可能不想选择它还有其他原因。

默认情况下,当您在 Visual Studio 中创建新项目时,它将使用 DLL 运行时。

运行时名称中的多线程是过去同时存在非线程安全和多线程 C/C++ 运行时的遗留物。您将始终在现代 Visual Studio 中使用多线程运行时,即使您自己的应用程序是单线程的。

部署 DLL 运行时

如果您要链接 DLL 运行时,那么您必须考虑在发布程序时如何部署它们(测试和客户)。

如果您向您的测试团队提供 Debug 构建,请确保您也提供 DLL 运行时的 Debug 变体。

此外,不要忘记获得正确的架构(例如 x86x64)。

可再发行安装程序

Microsoft 提供安装 Release(但不包括 Debug)DLL 的可再发行包。通过搜索 Visual C++ Redistributable 2013 之类的内容可以很容易地找到这些内容(将 Visual Studio 版本替换为您需要的内容)。您也可以直接访问微软网站上的Latest Supported Visual C++ Downloads

这些可再发行组件包是可执行文件,您可以从程序的安装程序中调用它们(或者您可以手动运行它们以设置测试环境)。请注意,x86 和 x64 有单独的可再发行组件。

合并模块

如果您正在构建 MSI 安装程序,您可以将您正在使用的 C/C++ 运行时的 Merge Module 合并到您的安装程序包中。这些合并模块通常位于C:\Program Files (x86)\Common Files\Merge Modules。例如,Visual Studio 2013 的 x86 C/C++ 运行时的合并模块称为Microsoft_VC120_CRT_x86.msm

请注意,这些合并模块名称中的版本号不是基于年份的产品版本,而是内部版本号。 This table on Wikipedia 显示版本号之间的映射以避免混淆。

与上面的独立可执行可再分发安装程序不同,合并模块也有调试变体。

Visual Studio 的某些版本支持 Visual Studio 安装程序 项目类型(在 Other Project TypesSetup and Deployment 类别下) ,并且如果您将程序项目的输出包含在这些安装程序之一中,则将自动包含运行时的合并模块。您还可以使用此技巧来获取安装程序,该安装程序将安装 Debug 运行时用于内部测试(任何虚拟 C/C++ 项目都可以,它不必实际安装您的程序) .

从 Redist 文件夹复制

您也可以将 DLL 从 Visual C++ 安装中的 redist 文件夹复制到安装程序的位置。例如,对于 Visual Studio 2013,您会在 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\x64\Microsoft.VC120.CRT\ 之类的某处找到 x64 C/C++ 运行时库。

调试变体可以在 C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\redist\Debug_NonRedist 之类的地方找到。

(根据您的 Visual Studio 版本和安装位置调整路径)。

混合 C/C++ 运行时

在理想情况下,您将使用相同的 C/C++ 运行时库变体(Debug vs ReleaseDLL vs 静态链接),并且全部来自相同版本的 Visual Studio,用于链接到您的程序中的所有库。

如果您混合运行时,您可能会遇到链接器错误,您的程序可能根本无法运行,或者更糟糕的是,它可能看起来正在运行,但仅在某些情况下会崩溃或给出错误结果。

忽略默认库

一个常见的情况是你只有一些第三方库的Release版本,但你仍然希望能够构建你自己的Debug变体使用这个库的代码。另一种情况是,您有一个使用静态 C/C++ 运行时的库,并且您希望在程序中使用 DLL 版本,反之亦然。

如果第三方库是 C 代码,那么您可能可以使用/NODEFAULTLIB linker option 来解决这个问题并让它真正工作。

如果库是 C++ 代码,那么您可能不太走运,因为生成的代码中有太多与特定运行时中的符号相关联。

这些是不同的库名称:

  • LIBCMT.LIB:静态链接的发布运行时(又名多线程
  • LIBCMTD.LIB:静态链接调试运行时(又名多线程调试
  • MSVCRT.LIB:动态链接的发布运行时(又名多线程 DLL)
  • MSVCRTD.LIB:动态链接调试运行时(又名多线程调试 DLL

请记住,您要忽略的运行时库是 第三方 代码正在使用的库,也就是说,它与您自己的程序正在使用的库不同。如果您查看构建输出,系统将提示您选择正确的输出,例如:

LINK : warning LNK4098: defaultlib 'LIBCMTD' conflicts with use of other libs; use /NODEFAULTLIB:library

您可以指定要忽略的库,方法是右键单击您的项目,选择 Properties,单击 Linker 下的 Input 条目并添加将运行时库名称添加到条目中。

跨越模块边界

您还可以跨模块边界(例如,在 EXE 和它加载的 DLL 之间)以更微妙的方式遇到 C/C++ 运行时不匹配问题。

例如,C 库中的数据结构可能由不同的运行时定义不同。我已经看到这会导致使用 DLL 的程序崩溃,该 DLL 在其 API 中使用了 FILE*。使用一个 C 运行时在一个模块中打开一个文件,并与另一个具有不同的、不兼容的实现的模块进行交互。更安全的选择包括为此类事情传递 Windows API HANDLE 对象,或者以与运行时无关的方式封装文件交互。

不同的运行时也使用自己的内存堆进行分配。如果一个对象在一个堆上的一个模块中分配,但在另一个模块中释放,如果 C/C++ 运行时不匹配,可能会发生崩溃。这当然只适用于使用 C 或 C++ 运行时的分配,例如 mallocnew。例如,如果到处都在使用默认的 Windows 进程堆,那么一切都很好。

请注意,如果模块静态链接到 C/C++ 运行时,即使它与它们链接的运行时的变体相同,它们也会遇到这个问题,因为仍然会有两个不同的运行时在它们自己的内存堆中玩。因此,在这种情况下,您将需要使用 DLL C/C++ 运行时。

如果运行时实现的功能(如异常或具有 vtable 的类)跨越模块边界,则在每个模块中使用(相同版本的)DLL 运行时也是明智之举,尽管某些不匹配的组合在实践中可能会起作用。

【讨论】:

    猜你喜欢
    • 2016-11-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-30
    相关资源
    最近更新 更多