复杂的C运行时库
因为各种原因常加一些第三方库到工程中。使用这些库,最好能够自己编译,否则应该真的应该好好检查一下它对C运行时库的依赖情况,否则真的容易出现莫名其名的访存错误。
总结一下,要检查以下几个方面:
1. 要检查一下依赖的C runtime lib是debug或release版,不要混用二者。
2. 检查C runtime lib是单线程版、静态多线程还是动态链版。
我们先看CRTLib (C运行库):
msdn 上有这样一段话:
警告 不要混合使用运行时库的静态版本和动态版本。在一个进程中有多个运行时库副本会导致问题,因为副本中的静态数据不与其他副本共享。链接器禁止在 .exe 文件内部既使用静态版本又使用动态版本链接,但您仍可以使用运行时库的两个(或更多)副本。例如,当与用动态 (DLL) 版本的运行时库链接的 .exe 文件一起使用时,用静态(非 DLL)版本的运行时库链接的动态链接库可能导致问题。(还应该避免在一个进程中混合使用这些库的调试版本和非调试版本)。
还有这样一张表:
Reusable Library Switch Library Macro(s) Defined
----------------------------------------------------------------
Single Threaded /ML LIBC (none)
Static MultiThread /MT LIBCMT _MT
Dynamic Link (DLL) /MD MSVCRT _MT and _DLL
Debug Single Threaded /MLd LIBCD _DEBUG
Debug Static MultiThread /MTd LIBCMTD _DEBUG and _MT
Debug Dynamic Link (DLL) /MDd MSVCRTD _DEBUG, _MT, and _DLL
这张表列出了Windows下有六种类型CRTLib(C运行库),它们是使用不同的switch生成的,这些switch通过宏起作用。
其中MT和MD都适用于多线程,其区别是:
1. MT为静态链接CRT,这样编译出来exe是自包含的,所以会相对大一些,但运行时不用再load CRT库。
2. MD为动态链接CRT,编译出来exe会小一些,运行时需要load CRT,性能有一点点损失。
任何工程都应该使用同样的CRT Library。即要么都是/ML,要么都是/MTD, 如此类推。 如果一个程序中混合使用不同类型的CRT,有时可以通过link,这样会存在不同CRT的copy,并会导致内存,比如:
1. 在一个lib中new出来内存,在另一个lib中delete,会crash。
2. 不能在多个lib中共享file handle,理由同上。
3. 一个lib中设置locale(本地化有关),不能在另一个lib中起作用。
其实对于Standard C++ Library也有类似的问题,我们这里同样列出一张表:
当链接程序时,一个C运行库会根据switch与你的程序链接,这是默认行为;但是如果你包含了C++标准头文件,比如<ios>那么Standard C++ Library会被链接进来。
msvcrt.dll 与 msvcr71.dll
还有一个比较常见的dll叫msvcr71.dll,将之与msvcrt.dll放在一起说。msvcrt.dll 是系统的一部分,它是为将来系统级组件预留的。我们的程序应该使用、发布的是msvcr71.dll,而且当发布时,最好把这个dll放在程序的目录下而非替换系统目录下的同名dll.
目前而言,这两个dll还是比较混乱的,如果你手上有个比较老的库依赖mscvrt.lib,那么其中依赖的dll可能是mscrt.dll,而如果你重新编译这个库的话会发现c运行库的依赖变成了mscrt71.dll。而把mscrt.dll和mscrt71.dll混用也是很不好的,同样可能出现我们上而引用的msdn那段话中讲的问题。
所以现在我在选择第三方库时会比较注意尽量选择开源的库;如果需要加入其他人编译的库,也会用比如dependence walker这样的工具先检查一下对C运行时的依赖情况。
推而广之一下,其实不仅C运行时库了,如果两个第三方库共同依赖了另外一个库,那么这个库的版本也应该被检查。
博客园上也还有不少文章与本文讲述的内容相近,比如以下两篇:
1. http://www.cnblogs.com/diyunpeng/archive/2011/06/16/2083085.html
2. http://www.cnblogs.com/Frodo/archive/2008/02/04/1064267.html