/MT、/MD编译选项,以及可能引起在不同堆中申请、释放内存的问题

/MT、/MD编译选项,以及可能引起在不同堆中申请、释放内存的问题

转自:http://www.cnblogs.com/cswuyg/archive/2012/02/03/2336424.html

一、MD(d)、MT(d)编译选项的区别

1、编译选项的位置

以VS2005为例,这样子打开:

1)         打开项目的Property Pages对话框

2)         点击左侧C/C++节

3)         点击Code Generation节

4)         右侧第六行Runtime Library项目

2、各个设置选项代表的含义

编译选项

包含

静态链接的lib

说明

/MD

_MT、_DLL

MSVCRT.lib

多线程、Release、DLL版本的运行时库  ---只能用于Release版本

/MDd

_DEBUG、_MT、_DLL

MSVCRTD.lib

多线程、Debug、DLL版本的运行时库  ----只能用于Debug版本

/MT

_MT

LIBCMT.lib

多线程、Release版本的运行时库

/MTd

_DEBUG、_MT

LIBCMTD.lib

多线程、Debug版本的运行时库

简单的说:

(1)/MD,表示运行时库由操作系统提供一个DLL,程序里不集成。

(2)/MT,表示运行时库由程序集成。

二、/MD、/MT的选择

      1、为什么选择/MD,不选/MT?

         (1)程序就不需要静态链接运行时库,可以减小软件的大小;

         (2)所有的模块都采用/MD,使用的是同一个堆,不存在A堆申请,B堆释放的问题;

   (3)用户机器可能缺少我们编译时使用的动态运行时库。(补充:如果我们软件有多个DLL,采用/MT体积增加太多,则可以考虑/MD + 自带系统运行时库)

      2、为什么选择/MT,不选择/MD?

         (1)有些系统可能没有程序所需要版本的运行时库,程序必须把运行时库静态链接上。

     (2)减少模块对外界的依赖。

      3、多个模块,必须选择相同的运行时库。

三、选择/MT需要解决的堆空间释放问题

         不同的模块各自有一份C运行时库代码、或者根本没有C运行时库,导致了各个模块会有各自的堆。如果在A堆中申请空间,到B堆中释放就会有崩溃,在模块A申请的空间,必须在模块A中释放。

         附录的DLL以及DLLUser代码,以STL的string为例,通过修改编译选项验证了这个问题。string在赋值的时候需要释放掉原来的内存空间,然后再申请新的内存空间存储新的内容,如果跨模块了,释放的时候就存在“A模块申请B模块释放”的问题,导致程序崩溃。

   (跨模块释放内存导致崩溃的内容,在《windows核心编程》第五版Page511谈DLL和进程的地址空间时有谈到)

四、选择/MD需要注意多个模块使用不同版本运行时库的问题

  (2012-9-17补充)

  (2013.5.30补充)

     多个dll被一个exe LoadLibrary加载,如果这些dll使用的运行时库是不同的,那么可能出现加载失败,原因可能是旧版本的运行时库已经在了,而某个dll它需要的是新版本的运行时库,旧版本不符合要求。

     如果工程里所有的模块都是自己写的或者可以完全控制的,那么这个问题不难解决,只需要在工程设置里都设置/MD,然后在相同的环境下编译一次就行。但是假如这个模块是外界提供的呢?

     可能存在这种情况:A动态库使用了B静态库,B静态库使用了C动态库,B静态库是外界提供的,我们要使用它,但无法修改它,我们也无法接触到C动态库。如果C动态库使用的运行时库版本跟编译A动态库的本地使用的不一致,那么A动态库里的嵌入信息就会记录两个不同版本的运行时库,它被加载的时候,可能会选择版本新的。假设A动态库被一个exe LoadLibrary加载,而这个exe本身的运行时库是旧的,这样就会导致A动态库加载失败,即便把新的运行时库拷贝到目录下也不行,因为exe这个进程已经加载了那个旧的运行时库。这时候必须使用manifest文件指定嵌入到A动态库里的运行时库为某个版本,忽略掉C动态库使用的运行时库版本。

     这个问题挺复杂的,我心思没去验证windows的PE文件加载会对运行时库做什么样的优先选择、运行时库在静态库里的记录…。只要记住,给外界使用的组件版本尽量避免使用/MD(这样会导致膨胀吗?据说,安装包可以做字节流式压缩)。

  附上另一个问题:静态库的依赖关系:exe-->libA-->libB,现在不想让exe接触到libB,于是把libA的librarian选项-->General选项-->Link Library Dependencies设置为Yes,这样即可,libA会包含libB,exe只需要接触libA。另外需要特别注意,libA对libB的依赖只需要且只能在Solution的Project Dependencies里设置,如果在libA的代码里写了”#pragma comment(lib, "libB.lib")”,会导致exe在link libA的时候提示找不到libA。如果exe还出现link错误,那一定是VS抽筋了:)

五、参考资料

1、微软关于MT、MD等的详细介绍

https://msdn.microsoft.com/zh-cn/library/2kzt1wy3(VS.80).aspx

2、不要出现A模块申请,B模块释放的情况

http://www.cnblogs.com/minggoddess/archive/2010/12/15/1907179.html

3、运行时库有哪些版本

http://www.cppblog.com/MichaelLiu/articles/10607.html

4、CSDN上关于堆空间释放的讨论

http://topic.csdn.net/t/20010112/09/57983.html

http://topic.csdn.net/t/20031009/17/2338051.html

http://topic.csdn.net/u/20090502/00/bf1602e3-ddf5-49b0-af81-8a23383f9ddc.html

http://blog.csdn.net/blz_wowar/article/details/2176536

5、不同模块不同的堆

http://www.cnblogs.com/WengYB/archive/2011/08/18/2144727.html

6、因为运行时库版本问题导致加载失败的分享

http://blog.csdn.net/dev_yarin/article/details/6768373

http://blog.163.com/henan_lujun/blog/static/19538333200611485511640/

7、《windows核心编程》

8、windows heap跟CRT heap

http://social.msdn.microsoft.com/Forums/vstudio/en-US/1e67edb1-9c2f-4f30-985a-7a0969366b1a/windows-heap-vs-crt-heap?forum=vcgeneral

http://msdn.microsoft.com/en-us/library/ms810466.aspx

附录:

Demo代码下载地址:https://files.cnblogs.com/cswuyg/Test_MD_and_MT.rar

文章二:VC 运行时库 /MD、/MDd 和 /MT、/MTd

http://blog.csdn.net/leepwang/article/details/8539414

有段时间在写cuda程序是出现过

error LNK2005: _exit 已经在 MSVCRTD.lib(MSVCR71D.dll) 中定义 等类似错误

原因应该是在vs2010 工程属性中 c/c++的代码生成(code generation)中的设置与cuda c/c++ Host Runtime library不一样导致的。
后来在写opencv程序中的 findcontour()函数时,又是碰到 多线程调试 (/MTD) 和 多线程调试dll(/MDd)等问题,所以特此查了些内容。

 

 

1、 http://qimo601.iteye.com/blog/1550348

这里总结下他们的区别,后面的那个'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在非必要时最好也不要采用了。

 

 

2、 http://www.cnblogs.com/eddyshn/archive/2009/11/23/1608823.html

C编译选项 多线程(/MT)
多线程调试(/MTd)
多线程 DLL (/MD)
多线程调试 DLL (/MDd)
C 运行时库                        库文件
Single thread(static link) ML            libc.lib
Debug single thread(static link) MLd        libcd.lib
MultiThread(static link) MT            libcmt.lib
Debug multiThread(static link) MTd            libcmtd.lib
MultiThread(dynamic link) MD            msvert.lib
Debug multiThread(dynamic link) MDd        msvertd.lib 
3. 各种 C 运行时库的区别
( 1 )静态链接的单线程库
静态链接的单线程库只能用于单线程的应用程序, C 运行时库的目标代码最终被编译在应用程序的二进制文件中。通过 /ML 编译选项可以设置 Visual C++ 使用静态链接的单线
程库。
( 2 )静态链接的多线程库
静态链接的多线程库的目标代码也最终被编译在应用程序的二进制文件中,但是它可以在多线程程序中使用。通过 /MT 编译选项可以设置 Visual C++ 使用静态链接的多线程库。
( 3 )动态链接的运行时库
动态链接的运行时库将所有的 C 库函数保存在一个单独的动态链接库 MSVCRTxx.DLL 中, MSVCRTxx.DLL 处理了多线程问题。使用 /MD 编译选项可以设置 Visual C++ 使用动态
链接的运行时库。
/MDd 、 /MLd 或 /MTd 选项使用 Debug runtime library( 调试版本的运行时刻函数库 ) ,与 /MD 、 /ML 或 /MT 分别对应。 Debug 版本的 Runtime Library 包含了调试信息
,并采用了一些保护机制以帮助发现错误,加强了对错误的检测,因此在运行性能方面比不上 Release 版本。 
下面是msdn关于Visual C++ 编译器选项的说明:
这些选项选择单线程或多线程运行时例程,指示多线程模块是否为 DLL,并选择运行时库的发布版本或调试版本。
选项     说明
/MD     定义 _MT 和 _DLL 以便同时从标准 .h 文件中选择运行时例程的多线程特定版本和 DLL 特定版本。此选项还使编译器将库名 MSVCRT.lib 放入 .obj 文件中。
用此选项编译的应用程序静态链接到 MSVCRT.lib。该库提供允许链接器解析外部引用的代码层。实际工作代码包含在 MSVCR71.DLL 中,该库必须在运行时对于与 MSVCRT.lib 链
接的应用程序可用。
当在定义了 _STATIC_CPPLIB (/D_STATIC_CPPLIB) 的情况下使用 /MD 时,它将导致应用程序通过静态多线程标准 C++ 库 (libcpmt.lib) 而非动态版本 (msvcprt.lib) 进行链接
,同时仍通过 msvcrt.lib 动态链接到主 CRT。
/MDd     定义 _DEBUG、_MT 和 _DLL,以便从标准 .h 文件中选择运行时例程的调试多线程特定版本和 DLL 特定版本。它还使编译器将库名 MSVCRTD.lib 放入 .obj 文件中。
/ML     使编译器将库名 LIBC.lib 放入 .obj 文件中,以便链接器使用 LIBC.lib 解析外部符号。这是编译器的默认操作。LIBC.lib 不提供多线程支持。
/MLd     定义 _DEBUG 并使编译器将库名 LIBCD.lib 放入 .obj 文件中,以便链接器使用 LIBCD.lib 解析外部符号。LIBCD.lib 不提供多线程支持。
/MT     定义 _MT,以便从标准头 (.h) 文件中选择运行时例程的多线程特定版本。此选项还使编译器将库名 LIBCMT.lib 放入 .obj 文件中,以便链接器使用 LIBCMT.lib 解析
外部符号。创建多线程程序需要 /MT     或 /MD(或它们的调试等效选项 /MTd 或 /MDd)。
/MTd     定义 _DEBUG 和 _MT。定义 _MT 会导致从标准 .h 文件中选择运行时例程的多线程特定版本。此选项还使编译器将库名 LIBCMTD.lib 放入 .obj 文件中,以便链接器使
用 LIBCMTD.lib 解析外部符号。创    建多线程程序需要 /MTd 或 /MDd(或它们的非调试等效选项 /MT 或 MD)。
/LD     创建 DLL。
将 /DLL 选项传递到链接器。链接器查找 DllMain 函数,但并不需要该函数。如果没有编写 DllMain 函数,链接器将插入返回 TRUE 的 DllMain 函数。
链接 DLL 启动代码。
如果命令行上未指定导出 (.exp) 文件,则创建导入库 (.lib);将导入库链接到调用您的 DLL 的应用程序。
将 /Fe 解释为命名 DLL 而不是 .exe 文件;默认程序名成为基名称.dll 而不是基名称.exe。
如果还未显式指定 /M 选项之一,则将默认运行时库支持更改为 /MT。
/LDd     创建调试 DLL。定义 _DEBUG。
    警告   不要混合使用运行时库的静态版本和动态版本。在一个进程中有多个运行时库副本会导致问题,因为副本中的静态数据不与其他副本共享。链接器禁止在 .exe 文件内
部既使用静态版本又使用动态版本链接,但您仍可以使用运行时库的两个(或更多)副本。例如,当与用动态 (DLL) 版本的运行时库链接的 .exe 文件一起使用时,用静态(非 
DLL)版本的运行时库链接的动态链接库可能导致问题。(还应该避免在一个进程中混合使用这些库的调试版本和非调试版本)。

 

 

3、 http://hi.baidu.com/musvision/item/80343d94ef7af3ecb725319a

 

 

IBCMTD.lib(crt0dat.obj) : error LNK2005: _exit 已经在 MSVCRTD.lib(MSVCR71D.dll) 中定义 等类似错误 
原因: 
Run-Time Library 
?Run-Time Library是编译器提供的标准库,提供一些基本的库函数和系统调用。 
我们一般使用的Run-Time Library是C Run-Time Libraries。当然也有Standard C++ libraries。 
C Run-Time Libraries实现ANSI C的标准库。VC安装目录的CRT目录有C Run-Time库的大部分源代码。 C Run-Time Libraries有静态库版本,也有动态链接库版本;有单线程版本,也有多线程版本;还有调试和非调试版本。
?动态链接库版本: 
/MD Multithreaded DLL 使用导入库MSVCRT.LIB
/MDd Debug Multithreaded DLL 使用导入库MSVCRTD.LIB 
?静态库版本: 
/ML Single-Threaded 使用静态库LIBC.LIB
/MLd Debug Single-Threaded 使用静态库LIBCD.LIB
/MT Multithreaded 使用静态库LIBCMT.LIB
/MTd Debug Multithreaded 使用静态库LIBCMTD.LIB 
若要使用此运行时库  请忽略这些库  
单线程 (libc.lib)  libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib  
多线程 (libcmt.lib)  libc.lib、msvcrt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib  
使用 DLL 的多线程 (msvcrt.lib)  libc.lib、libcmt.lib、libcd.lib、libcmtd.lib、msvcrtd.lib  
调试单线程 (libcd.lib)  libc.lib、libcmt.lib、msvcrt.lib、libcmtd.lib、msvcrtd.lib  
调试多线程 (libcmtd.lib)  libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、msvcrtd.lib  
使用 DLL 的调试多线程 (msvcrtd.lib)  libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib 

解决方法: 
属性,链接器,输入,忽略指定库 libc.lib、libcmt.lib、msvcrt.lib、libcd.lib、libcmtd.lib (使用一个,忽略其他的。还可以尝试其他编译选象)

 

posted @ 2015-09-10 13:32  flylong0204  阅读(1040)  评论(0)    收藏  举报