VS项目属性中的C/C++运行库:MT、MTd、MD、MDd
文章转载自:http://blog.csdn.net/ithzhang/article/details/20160009
在各个版本的编译器中,我们可以通过配置选项来设置程序使用的C和C++运行时库的类型。如下图(其他版本编译器大同小异):
MT选项:链接LIB版的C和C++运行库。在链接时就会在将C和C++运行时库集成到程序中成为程序中的代码,程序体积会变大。
MTd选项:LIB的调试版。
MD选项:使用DLL版的C和C++运行库,这样在程序运行时会动态的加载对应的DLL,程序体积会减小,缺点是在系统没有对应DLL时程序无法运行。
MDd选项:表示使用DLL的调试版。
关于C和C++运行库的概念
为了提高C语言的开发效率,C标准定义了一系列常用的函数,称为C库函数。C标准仅仅定义了函数原型,并没有提供实现。因此这个任务留给了各个支持C语言标准的编译器。每个编译器通常实现了标准C的超集,称为C运行时库(C Run Time Library),简称CRT。对于VC++编译器来说,它提供的CRT库支持C标准定义的标准C函数,同时也有一些专门针对Windows系统特别设计的函数
与C语言类似,C++也定义了自己的标准,同时提供相关支持库,称为C++运行时库或C++标准库。由于C++对C的兼容性,C++标准库包括了C标准库,除此之外还包括了IO流和标准模板库STL。
VC++在何处实现C和C++运行库
VC++完美的支持C和C++标准,因此也就按照C和C++标准定义的函数原型实现了上述运行时库。为了方便有不同需求的客户使用,VC++分别实现了动态链接库DLL版本和静态链接库LIB版本。同时为了支持程序调试且不影响程序的性能,又分别提供了对应的调试版本。调试版本的名称在Release版本名称后添加了字母d。
对于C运行时库CRT,VC6.0、VC2005、VC2008和VC2010均提供了DLL版本和LIB版本。
上述各个编译器提供的LIB版的CRT库,均实现在libcmt.lib。对应的调试版名称为libcmtd.lib。
而DLL版本名称根据编译器不同而不同,我们可以从名称上加以分辨。
VC6.使用的CRT库的DLL版本在MSVCRT.DLL中实现, 对应调试版本为MSVCRTD.LIB。
VC2005使用的CRT库的DLL版本在MSVCR80.DLL中实现,对应调试版本为MSVCR80.DLL。
VC2008使用的CRT库的DLL版本在MSVCR90.DLL中实现,对应调试版本为MSVCR90D.DLL。
VC2010使用的CRT库的DLL版本在MSVCR100.DLL中实现,对应调试版本为MSVCR100D.DLL。
VC2013使用的CRT库的DLL版本在MSVCR120.DLL中实现,对应调试版本为MSVCR120D.DLL。
对于C++标准库中的IO流和STL,VC6.0、VC2005、VC2008和VC2010也提供了DLL版本和LIB版本。
LIB版均实现在libcpmt.lib中,对应的调试版本为libcpmtd.lib。
不同版本的编译器实现的DLL也不相同。
VC6.使用的C++类库的 DLL版本在MSVCP60.DLL中实现, 对应调试版本为MSVCP60D.LIB。
VC2005使用的C++类库的DLL版本在MSVCP80.DLL中实现,对应调试版本为MSVCP80.DLL。
VC2008使用的C++类库的 DLL版本在MSVCP90.DLL中实现,对应调试版本为MSVCP90D.DLL。
VC2010使用的C++类库的DLL版本在MSVCP100.DLL中实现,对应调试版本为MSVCP100D.DLL。
动态版(DLL)和静态版(LIB)C和C++运行库的优缺点
因为静态版必须把C和C++运行库复制到目标程序中(.exe),所以产生的可执行文件会比较大。同时对于使用多个模块的大型软件来说,如果每个模块均选择静态链接C或C++运行库,在程序运行时就会存在多个运行库。在链接时也会出现重复定义的问题,如图所示。
使用DLL版的C和C++运行库,程序在运行时动态的加载对应的DLL。程序体积变小,但一个很大的问题就是一旦找不到对应DLL,程序将无法运行(比如所要移植的电脑没有安装VC++)。假设使用VC6.0并选择使用MD选项构建,那么当用户使用VC2005来使用这个DLL时很可能出现找不到MSVCRT.DLL或MSVCP60.DLL的情况。
程序运行时出现问题,选择的是Release,win64位的模式,并且已经看到了宏定义NDEBUG,但是程序依然进入上面的部分
解决方案是将属性->C/C++->代码生成器->运行库里面的多线程调试(/MTD)修改为多线程DLL(/MD)即可
修改之后:
编译一下
解释原因来自 http://blog.csdn.net/u013829933/article/details/50321355,感谢~~
这里总结下他们的区别,后面的那个'd'是代表DEBUG版本,没有'd'的就是RELEASE版本了。
首先说/MT
/MT是 "multithread, static version ” 意思是多线程静态的版本,定义了它后,编译器把LIBCMT.lib 安置到OBJ文件中,让链接器使用LIBCMT.lib 处理外部符号。
/MD是 "multithread- and DLL-specific version” ,意思是多线程DLL版本,定义了它后,编译器把 MSVCRT.lib 安置到OBJ文件中,它连接到DLL的方式是静态链接,实际上工作的库是MSVCR80.DLL。
即:
静态运行时库:LIBCMT.lib
动态运行时库:MSVCRT.lib + MSVCR80.DLL
所以,当你用CMAKE生成工程文件时,若CMAKE是用/MT生成的(查看工程原始目录的CMakeLists.txt),则它所调用的运行时库为:LIBCMT.lib,若生成的工程的运行时库(Runtime Library)你选择/MD,则此工程在编译后链接的时候,将会调用动态运行时库:MSVCRT.lib + MSVCR80.DLL,明显,两次对同一个某运行时库里的函数调用的库不同,则会出现重定义的错误。若此工程生成的是库文件,则其他工程调用此库时也必须是/MT。
===============================================
VC项目属性→配置属性→C/C++→代码生成→运行时库 可以采用的方式有:多线程(/MT)、多线程调试(/MTd)、多线程DLL(/MD)、多线程调试DLL(/MDd)、单线程(/ML)、单线程调试(/MLd)。
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 |
其中以小写“d”结尾的选项表示的DEBUG版本的,没有“d”的为RELEASE版本。大型项目中必须要求所有组件和第三方库的运行时库是统一的,否则将会出现LNK2005井喷。
单线程运行时库选项/ML和/MLd在VS2003以后就被废了。
/MT和/MTd表示采用多线程CRT库的静态lib版本。该选项会在编译时将运行时库以静态lib的形式完全嵌入。该选项生成的可执行文件运行时不需要运行时库dll的参加,会获得轻微的性能提升,但最终生成的二进制代码因链入庞大的运行时库实现而变得非常臃肿。当某项目以静态链接库的形式嵌入到多个项目,则可能造成运行时库的内存管理有多份,最终将导致致命的“Invalid Address specified to RtlValidateHeap”问题。另外托管C++和CLI中不再支持/MT和/MTd选项。
/MD和/MDd表示采用多线程CRT库的动态dll版本,会使应用程序使用运行时库特定版本的多线程DLL。链接时将按照传统VC链接dll的方式将运行时库MSVCRxx.DLL的导入库MSVCRT.lib链接,在运行时要求安装了相应版本的VC运行时库可再发行组件包(当然把这些运行时库dll放在应用程序目录下也是可以的)。 因/MD和/MDd方式不会将运行时库链接到可执行文件内部,可有效减少可执行文件尺寸。当多项目以MD方式运作时,其内部会采用同一个堆,内存管理将被简化,跨模块内存管理问题也能得到缓解。
结论:/MD和/MDd将是潮流所趋,/ML和/MLd方式请及时放弃,/MT和/MTd在非必要时最好也不要采用了。
介绍一下C和C++运行库,只因发现工作几年的人对此一知半解的大有人在。
在使用VC构建项目时,经常会遇到下面的链接错误:
初学者面对这些错误常常不知所错:libcmt.lib是什么东西?msvcrtd.dll又是干吗用的?在使用VC++时我们也常常对下面的运行库配置项感到疑惑,它们到底究竟是什么意思呢?甚至一些工作了很多年的程序员也对此一知半解。今天让我们来了解一下它们。
从C和C++运行库说起
为了提高C语言的开发效率,C标准定义了一系列常用的函数,称为C库函数。C标准仅仅定义了函数原型,并没有提供实现。因此这个任务留给了各个支持C语言标准的编译器。每个编译器通常实现了标准C的超集,称为C运行时库(C Run Time Libray) ,简称CRT。对于VC++编译器来说,它提供的CRT库支持C标准定义的标准C函数,同时也有一些专门针对windows系统特别设计的函数。
与C语言类似,C++也定义了自己的标准,同时提供相关支持库,我们把它称为C++运行时库或C++标准库。
由于C++对C的兼容性,C++标准库包括了C标准库,除此之外还包括IO流和标准模板库STL。
VC++在何处实现C和C++运行库
VC++完美的支持C和C++标准,因此也就按照C和C++的标准定义的函数原型实现了上述运行时库。为了方便有不同需求的客户的使用,VC++分别实现了动态链接库DLL版本和静态库LIB版本。同时为了支持程序调试且不影响程序的性能,又分别提供了对应的调试版本。调试版本的名称在Release版本名称后添了字母d。
对于C运行时库CRT,VC6.0、VC2005、VC2008和VC2010均提供了DLL版本和LIB版本。上述各个编译器提供的LIB版的CRT库,均实现在libcmt.lib。对应的调试版名称为libcmtd.lib。
而DLL版本名称根据编译器不同而不同,我们可以从名称上加以分辨。
VC6.使用的CRT库的DLL版本在MSVCRT.DLL中实现, 对应调试版本为MSVCRTD.LIB。
VC2005使用的CRT库的DLL版本在MSVCR80.DLL中实现,对应调试版本为MSVCR80.DLL。
VC2008使用的CRT库的DLL版本在MSVCR90.DLL中实现,对应调试版本为MSVCR90D.DLL。
VC2010使用的CRT库的DLL版本在MSVCR100.DLL中实现,对应调试版本为MSVCR100D.DLL。
C++标准兼容C标准,但VC各版本将C++编译器使用的C标准库与C编译器使用的C运行库一起实现,它们使用相同的运行库。
对于C++标准库中的IO流和STL,VC6.0、VC2005、VC2008和VC2010也提供了DLL版本和LIB版本。
LIB版均实现在libcpmt.lib中,对应的调试版本为libcpmtd.lib。
不同版本的编译器实现的DLL也不相同。
VC6.使用的C++类库的 DLL版本在MSVCP60.DLL中实现, 对应调试版本为MSVCP60D.LIB。
VC2005使用的C++类库的DLL版本在MSVCP80.DLL中实现,对应调试版本为MSVCP80.DLL。
VC2008使用的C++类库的 DLL版本在MSVCP90.DLL中实现,对应调试版本为MSVCP90D.DLL。
VC2010使用的C++类库的DLL版本在MSVCP100.DLL中实现,对应调试版本为MSVCP100D.DLL。
在各个版本的编译器中,我们可以通过配置选项来设置程序使用的C和C++运行时库的类型。如下图(其他版本编译器大同小异):
MT选项:链接LIB版的C和C++运行库。在链接时就会在将C和C++运行时库集成到程序中成为程序中的代码,程序体积会变大。
MTd选项:LIB的调试版。
MD选项:使用DLL版的C和C++运行库,这样在程序运行时会动态的加载对应的DLL,程序体积会减小,缺点是在系统没有对应DLL时程序无法运行。
MDd选项:表示使用DLL的调试版。
在 《由使用LeakDialog时遇到的问题而引出的一些分析》 这篇文章中的实验一,使用VC6.0的默认配置没有拦截到内存泄露。其原因是VC6.0的控制台项目默认配置是静态链接CRT库(单线程版,后面会介绍)。
动态版(DLL)和静态版(LIB)C和C++运行库的优缺点
因为静态版必须把C和C++运行库复制到目标程序中,所以产生的可执行文件会比较大。同时对于使用多个模块的大型软件来说,如果每个模块均选择静态链接C或C++运行库,在程序运行时就会存在多个运行库。在链接时也会出现重复定义的问题,如文章首第一张图所示。
使用DLL版的C和C++运行库,程序在运行时动态的加载对应的DLL。程序体积变小,但一个很大的问题就是一旦找不到对应DLL,程序将无法运行。假设使用VC6.0并选择使用MD选项构建,那么当用户使用VC2005来使用这个DLL时很可能出现找不到MSVCRT.DLL或MSVCP60.DLL的情况。
在这里介绍一个很好的工具:Dependency Walker,可以用来分析DLL的依赖关系,同时查看DLL导出的函数,使用方法请Google。
使用该工具打开MSVCRT.DLL,如下图:
我们可以在其中找到我们经常使用使用的C函数,如printf ,getchar,malloc等。
打开MSVCP100.DLL,也可以找到这些C函数:
在开发的过程中我们也会遇到如下图的链接错误,LIBCD.lib究竟是何方神圣呢?
它其实是LIBC.lib的调试版,而LIBC.lib是只有在VC6.0才会使用的静态库,该库是CRT的单线程版,用于支持单线程版本的CRT。VC2005等更高版本的编译器已经不再提供单线程版本,转而使用多线程版的MSVCR80.DLL或libcmt.lib。
当遇到上述符号定义冲突的链接错误时,可以选择忽略libcd.lib。
2014.2.28 于浙江杭州
测试VS中【项目属性】-【C/C++】-【代码生成】选项中的【运行库】- 【 /MT, /MTd, /MD, /MDd】不同的选择对C/C++运行库的影响:
环境: windows7 64位 + VS2010
项目类型:win32控制台项目
查看工具:depends
下面表格中为使用depends查看到的结果
Debug C++ Runtime C Runtime
--------------------------------------------------------------------------
/MDd msvcp100d.dll msvcr100d.dll
/MD msvcp100d.dll msvcr100.dll
/MTd 无 无
/MT 无 无
--------------------------------------------------------------------------
Release C++ Runtime C Runtime
--------------------------------------------------------------------------
/MDd msvcp100d.dll msvcr100d.dll
/MD msvcp100.dll msvcr100.dll
/MTd 无 无
/MT 无 无
--------------------------------------------------------------------------
总结:软件发布时可以使用release的 /MD选项,这样可以减少exe体积。
也可以使用release, /MT版本。