CRT

_伊万 2013-11-19 10:26:50
MSVC CRT
相比于相对自由分散的glibc,一直伴随着不同版本的Visual C++发布的MSVC CRT(Microsoft Visual C++ C Runtime)倒看过去更加有序一些。从1992年最初的Visual C++ 1.0版开始,一直到现在的Visual C++ 9.0(又叫做Visual C++ 2008),MSVC CRT也从1.0版发展到了9.0版。

同一个版本的MSVC CRT根据不同的属性提供了多种子版本,以供不同需求的开发者使用。按照静态/动态链接,可以分为静态版和动态版;按照单线程/多线程,可以分为单线程版和多线程版;按照调试/发布,可分为调试版和发布版;按照是否支持C++分为纯C运行库版和支持C++版;按照是否支持托管代码分为支持本地代码/托管代码和纯托管代码版。这些属性很多时候是相互正交的,也就是说它们之间可以相互组合。比如可以有静态单线程纯C纯本地代码调试版;也可以有动态的多线程纯C纯本地代码发布版等。但有些组合是没有的,比如动态链接版本的CRT是没有单线程的,所有的动态链接CRT都是多线程安全的。

这样的不同组合将会出现非常多的子版本,于是微软提供了一套运行库的命名方法。这个命名方法是这样的,静态版和动态版完全不同。静态版的CRT位于MSVC安装目录下的lib/,比如Visual C++ 2008的静态库路径为“Program Files/Microsoft Visual Studio 9.0/VC/lib”,它们的命名规则为:

libc [p] [mt] [d] .lib

l p 表示 C Plusplus,即C++标准库。

l mt表示 Multi-Thread,即表示支持多线程。

l d 表示 Debug,即表示调试版本。

比如静态的非C++的多线程版CRT的文件名为libcmtd.lib。动态版的CRT的每个版本一般有两个相对应的文件,一个用于链接的.lib文件,一个用于运行时用的.dll动态链接库。它们的命名方式与静态版的CRT非常类似,稍微有所不同的是,CRT的动态链接库DLL文件名中会包含版本号。比如Visual C++ 2005的多线程、动态链接版的DLL文件名为msvcr90.dll(Visual C++ 2005的内部版本号为8.0)。表11-1列举了一些最常见的MSVC CRT版本(以Visual C++ 2005为例)。

表11-1

文件名

相关的DLL

属性

编译器选项

预编译宏

libcmt.lib



多线程,静态链接

/MT

_MT

msvcrt.lib

msvcr80.dll

多线程,动态链接

/MD

_MT, _DLL

libcmtd.lib



多线程,静态链接,调试

/MTd

_DEBUG, _MT

msvcrtd.lib

msvcr90d.dll

多线程,动态链接,调试

/MDd

_DEBUG, _MT, _DLL

msvcmrt.lib

msvcm90.dll

托管/本地混合代码

/clr



msvcurt.lib

msvcm90.dll

纯托管代码

/clr:pure



自从Visual C++ 2005(MSVC 8.0)以后,MSVC不再提供静态链接单线程版的运行库(LIBC.lib、LIBCD.lib),因为据微软声称,经过改进后的新的多线程版的C运行库在单线程的模式下运行速度已经接近单线程版的运行库,于是没有必要再额外提供一个只支持单线程的CRT版本。

默认情况下,如果在编译链接时不指定链接哪个CRT,编译器会默认选择LIBCMT.LIB,即静态多线程CRT,Visual C++ 2005之前的版本会选择LIBC.LIB,即静态单线程版本。关于CRT的多线程和单线程的问题,我们在后面的章节还会再深入分析。

Q&A

Q:如果一个程序里面的不同obj文件或DLL文件使用了不同的CRT,会不会有问题?

A:这个问题实际上分很多种情况。如果程序没有用到DLL,完全静态链接,不同的obj在编译时用到了不同版本的静态CRT。由于目前静态链接CRT只有多线程版,并且如果所有的目标文件都统一使用调试版或发布版,那么这种情况下一般是不会有问题的。因为我们知道,目标文件对静态库引用只是在目标文件的符号表中保留一个记号,并不进行实际的链接,也没有静态库的版本信息。

但是,如果程序涉及动态链接CRT,这就比较复杂了。因为不同的目标文件如果依赖于不同版本的msvcrt.lib和msvcrt.dll,甚至有些目标文件是依赖于静态CRT,而有些目标文件依赖于动态CRT,那么很有可能出现的问题就是无法通过链接。链接器对这种情况的具体反应依赖于输入目标文件的顺序,有些情况下它会报符号重复定义错误:

MSVCRTD.lib(MSVCR80D.dll) : error LNK2005: _printf already defined in LIBCMTD.lib (printf.obj)

但是有些情况下,它会使链接顺利通过,只是给出一个警告:

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

如果碰到上面这种静态/动态CRT混合的情况,我们可以使用链接器的/NODEFAULTLIB来禁止某个或某些版本的CRT,这样一般就能使链接顺利进行。

最麻烦的情况应该属于一个程序所依赖的DLL分别使用不同的CRT,这会导致程序在运行时同时有多份CRT的副本。在一般情况下,这个程序应该能正常运行,但是值得注意的是,你不能够在这些DLL之间相互传递使用一些资源。比如两个DLL A和B分别使用不同的CRT,那么应该注意以下问题:

l 不能在A中申请内存然后在B中释放,因为它们分属于不同的CRT,即拥有不同的堆,这包括C++里面所有对象的申请和释放;

l 在A中打开的文件不能在B中使用,比如FILE*之类的,因为它们依赖于CRT的文件操作部分。

还有类似的问题,比如不能相互共享locale等。如果不违反上述规则,可能会使程序发生莫名其妙的错误并且很难发现。

防止出现上述问题的最好方法就是保证一个工程里面所有的目标文件和DLL都使用同一个版本的CRT。当然有时候事实并不能尽如人意,比如很多时候当我们要用到第三方提供的.lib或DLL文件而对方又不提供源代码时,就会比较难办。

Windows系统的system32目录下有个叫msvcrt.dll的文件,它跟msvcr90.dll这样的DLL有什么区别?

Q:为什么我用Visual C++ 2005/2008编译的程序无法在别人的机器上运行?

A:因为Visual C++ 2005/2008编译的程序使用了manifest机制,这些程序必须依赖于相对应版本的运行库。一个解决的方法就是使用静态链接,这样就不需要依赖于CRT的DLL。另外一个解决的方法就是将相应版本的运行库与程序一起发布给最终用户。
...全文
88 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

18,141

社区成员

发帖
与我相关
我的任务
社区描述
Windows客户端使用相关问题交流社区
社区管理员
  • Windows客户端使用社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧