glibc与MSVC CRT,crt编译错误及解决

11.2.3 glibc与MSVC CRT(4)

11.2.3  glibc与MSVC CRT(4)

当你在程序里包含了某个C++标准库的头文件时,MSVC编译器就认为该源代码文件是一个C++源代码程序,它会在编译时根据编译选项,在目标文件 的".drectve"段(还记得第2章中的DIRECTIVE吧?)相应的C++标准库链接信息。比如我们用C++写一个"Hello World"程序:

// hello.cpp
#include <iostream>
int main()
{
std::cout << "Hello world" << std::endl;
return 0;
}

然后将它编译成目标文件,并查看它的".drectve"段的信息:

cl /c hello.cpp
dumpbin /DIRECTIVES hello.obj
Microsoft (R) COFF/PE Dumper Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file msvcprt.obj
File Type: COFF OBJECT
   Linker Directives
-----------------
/DEFAULTLIB:"libcpmt"
/DEFAULTLIB:"LIBCMT"
/DEFAULTLIB:"OLDNAMES"
cl /c /MDd hello.cpp
dumpbin /DIRECTIVES hello.obj
Microsoft (R) COFF/PE Dumper Version 9.00.21022.08
Copyright (C) Microsoft Corporation.  All rights reserved.

Dump of file msvcprt.obj
File Type: COFF OBJECT
   Linker Directives
-----------------
/manifestdependency:"type='win32'
name='Microsoft.VC90.DebugCRT'
version='9.0.21022.8'
processorArchitecture='x86'
publicKeyToken='1fc8b3b9a1e18e3b'"
/DEFAULTLIB:"msvcprtd"
/manifestdependency:"type='win32'
name='Microsoft.VC90.DebugCRT'
version='9.0.21022.8'
processorArchitecture='x86'
publicKeyToken='1fc8b3b9a1e18e3b'"
/DEFAULTLIB:"MSVCRTD"
/DEFAULTLIB:"OLDNAMES"

可以看到,hello.obj须要链接libcpmt.lib、LIBCMT.lib和OLDNAMES.lib。当我们使用"/MDd"参数编译 时,hello.obj就需要msvcprtd.lib、MSVCRTD.lib和OLDNAMES.lib,除此之外,编译器还给链接器传递 了"/manifestdependency"参数,即manifest信息。

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,那么应该注意以下问题:

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

在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。另外一个解决的方法就是将相应版本的运行库与程序一起发布给最终用户。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值