我正在努力更好地掌握Visual Studio 2013中的CRT库选项(C ++ - >代码生成 - >运行时库)以及如何知道选择哪个选项(以及何时更改默认值)
来自MSDN:
可重用库及其所有用户应使用相同的CRT库类型,因此应使用相同的编译器开关。
因此,我的理解是,如果您要链接第三方库,则应使用与构建库相同的CRT版本。构建库的人应该指定构建中使用的CRT选项。
有没有办法通过查看.lib文件来确定使用哪个CRT版本?
更重要的是,如果您没有链接任何第三方库,您将如何决定使用哪个选项?您何时考虑更改默认值?
答案 0 :(得分:8)
简短回答:
所以,我的理解是,如果你正在与第三方联系 库,您应该使用与构建时相同的CRT版本 图书馆。构建库的人应该指定什么CRT选项 用于构建。
这是最容易出错的选项。可以混合运行时,但如果你这样做,你可能会遇到意想不到的错误。
有没有办法通过查看来确定使用哪个CRT版本 在.lib文件?
我自己不了解.lib
,但如果第三方代码有DLL或EXE,您可以看到它依赖于哪些CRT DLL使用Windows {{ 3}}工具。
如果它是静态库,并且您的代码的CRT选项不匹配,那么您在构建时会看到警告。
更重要的是,您如何决定使用哪个选项? 是否与任何第三方图书馆建立链接?你什么时候考虑? 更改默认值?
对于最简单的部署,静态链接是最好的;你可以自己运送可执行文件,它将运行。对于具有多个EXE和DLL的较大项目,如果静态链接,则代码大小会更大。如果在同一进程中有多个模块(EXE加上1个或更多自己的DLL),那么最好共享相同的CRT代码。
更多细节(基于我之前写过的博文):
您可以使用以下四种C / C ++运行时库变体来构建代码:
您可以通过在Visual Studio中右键单击项目并选择“属性”,单击 C / C ++ 下的代码生成选项来选择您正在使用的库。弹出的对话框,然后转到运行时库属性。
请记住,此设置是按配置进行的,因为您需要为调试配置选择调试运行时库,并且发布 发布配置的运行时库。
有什么区别?
DLL 运行时库选项意味着您可以动态链接到C / C ++运行时,并且要运行程序, DLL 将需要位于程序的某个位置可以找到它(稍后会详述)。
不提及DLL(多线程调试和多线程发布)的选项会导致程序与运行时静态链接。这意味着您不需要外部DLL来运行程序,但由于额外的代码,您的程序会更大,还有其他原因可能导致您不想选择它。
默认情况下,在Visual Studio中创建新项目时,它将使用 DLL 运行时。
运行时名称中的多线程是过去非线程安全和多线程C / C ++运行时的遗留问题。即使您自己的应用程序是单线程的,您也将始终在现代Visual Studio中使用多线程运行时。
如果您要链接DLL运行时,那么在发布程序时(考试和客户),您必须考虑如何部署它们。
如果您向测试团队提供 Debug 版本,请确保同时提供DLL运行时的Debug变体。
另外,不要忘记获得正确的架构(例如 x86 vs x64 )。
Microsoft提供可再发行的软件包,用于安装发行版(但不包括调试)DLL。通过搜索 Visual C ++ Redistributable 2013 (将Visual Studio版本替换为您需要的内容),可以轻松找到这些内容。您也可以直接访问微软网站上的Dependency Walker。
这些可再发行组件包是可执行文件,您可以从程序的安装程序中调用它们(或者您可以手动运行它们以设置测试环境)。请注意,x86和x64有单独的可再发行组件。
如果您正在构建MSI安装程序,则可以将您正在使用的C / C ++运行时的Latest Supported Visual C++ Downloads合并到安装程序包中。这些合并模块通常位于C:\Program Files (x86)\Common Files\Merge Modules
中。例如,Visual Studio 2013的x86 C / C ++运行时合并模块称为Microsoft_VC120_CRT_x86.msm
。
请注意,这些合并模块名称中的版本号不是基于年份的产品版本,而是内部版本号。 Merge Module显示了版本号之间的映射,以避免混淆。
与上面的独立可执行可再发行安装程序不同,合并模块也有Debug变体。
某些版本的Visual Studio支持 Visual Studio Installer 项目类型(在其他项目类型中的设置和部署类别下) ,如果在其中一个安装程序中包含程序项目的输出,则将自动包含运行时的合并模块。你也可以使用它作为一个技巧来获得一个安装程序,它将安装 Debug 运行时用于内部测试目的(任何虚拟C / C ++项目都可以,它不必实际安装你的程序)
您也可以将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 ++运行时库变体(调试 vs 发布, DLL vs 静态链接),以及所有来自相同版本的Visual Studio,用于链接到程序中的所有库。
如果你混合运行时,你可能会遇到链接器错误,你的程序可能根本无法运行,或者更糟糕的是,它可能看起来有效但崩溃或仅在某些情况下给出错误的结果。
常见的情况是您只拥有某些第三方库的发布版本,但您仍希望能够构建自己的 Debug 版本使用此库的代码。另一种情况是,您有一个使用静态C / C ++运行时的库,并且您希望在程序中使用 DLL 版本,反之亦然。
如果第三方库是C代码,那么您可能可以使用This table on Wikipedia来摆脱这种情况并让它真正起作用。
如果库是C ++代码,那么您可能会运气不好,因为生成的代码太多与特定运行时中的符号相关联。
这些是不同的库名称:
请记住,您要忽略的运行时库是第三方代码正在使用的运行时库,即它将与您自己的程序正在使用的库不同。如果你查看你的构建输出,你将被提示选择正确的构建输出,例如:
LINK : warning LNK4098: defaultlib 'LIBCMTD' conflicts with use of other libs; use /NODEFAULTLIB:library
您可以通过右键单击项目,选择属性,单击链接器下的输入条目并添加来指定要忽略的库将运行时库名称放入条目中。
您还可以跨模块边界以更微妙的方式遇到C / C ++运行时不匹配问题(例如,在EXE和它加载的DLL之间)。
例如,C库中的数据结构可以通过不同的运行时来不同地定义。我已经看到这个原因在使用DLL的程序中崩溃,该DLL在其API中使用了FILE*
。使用一个C运行时在一个模块中打开文件,并与另一个具有不同,不兼容的实现的模块进行交互。更安全的选项包括为此类事件传递Windows API HANDLE
对象,或者以与运行时无关的方式包装文件交互。
不同的运行时也使用自己的内存堆进行分配。如果一个对象在一个堆上的一个模块中分配,但在另一个模块中解除分配,则如果C / C ++运行时不匹配,则可能发生崩溃。这当然仅适用于使用C或C ++运行时的分配,例如malloc
或new
。例如,如果在任何地方都使用默认的Windows进程堆,那么一切都很好。
请注意,如果模块静态地链接到C / C ++运行时,即使它与它们链接的运行时的变体相同,它们也会遇到此问题,因为仍然会有两个不同的运行时具有自己的内存堆玩。因此,在这种情况下,您将需要使用DLL C / C ++运行时。
如果运行时实现的功能(如带有vtable的异常或类)跨越模块边界,那么在每个模块中使用(相同版本)DLL运行时也是明智的,尽管某些不匹配的组合可能在实践中起作用。