多线程MT加载的是静态运行时库,属于C语言版本;而多线程MD版本加载是动态运行时库,属于微软版本。在工程属性窗体中选择配置属性,C++选项的代码生成多线程调试 DLL (/MDd)和多线程调试 DLL (/MTd)是Debug版本,后面没有d的是Release版本。
一、全局堆句柄不一样。
网上有一个说法,就是一个线程一个栈,一个模块一个堆。前者很容易有理解,每个线程创建的时候在CreateThread中都能制定默认栈大小,只是很多情况下都取了默认值。而一个模块一个堆呢?其实很简单测试,如果是一个多线程MT编译方式的程序,你写一个dll,导出一个函数,参数设置为vector<int>,然后在exe中调用,当导出函数结束时就会崩溃掉。其实原因很简单,就是因为初始化向量空间时malloc内存的过程在exe中,而vector析构时会free内存,申请和释放的模块不一致而违背了一个模块一个堆的说法。
细心者会发现,其实不管是new/delete还是malloc/free最终调用的都是HeapAlloc/HeapFree,而这个函数的第一个参数为一个全局的堆句柄,由CreateHeap创建,创建该全局堆句柄的尚且在main等系列主函数之前。事实上这种夸模块堆操作异常总结起来就是申请内存时HeapAlloc传入的句柄和释放该内存时HeapFree传入的句柄不一致引起的,读者可写代码测试。
但是以上问题如果是多线程MD编译方式下便可解决,也就是说如果都是通过多线程MD编译方式出来的程序,如果是A模块中申请的内存到B模块中释放不会出现问题。
二、链接的运行时库不同。
对于多线程MT的程序来说,其连接的是libcmt.lib,该文件属于C语言运行时库,整个lib都会连接到PE文件当中。而多线程MD的程序链接的却是类似msvcpXXX.dll,该文件属于微软运行时库.也就是说如果是多线程MD编译出来的文件运行时都会加载相应版本的运行时库,当如果找不到运行时库就会报错而无法运行,同时如果运行时库不匹配也会出现各种意料之外的崩溃或者程序根本跑不起来等情况。
三、编译出来的PE文件大小区别
此时如果两者作为对比就会很明显看到多线程MT编译出来的文件体积要比多线程MD编译出来的大,因为MT是把对应的运行时库直接放到编译出来的PE文件当中,而MD却是运行的时候从第三方dll中获取运行时库,自己本身却不包含。同时另外的区别也很明显,多线程MT编译出来的文件运行时不需要加载第三方dll所以运行效率要比多线程MD稍微高一点点,当然作为用户是完全感觉不到的。所以说如果打开一个程序目录,发现里面有类似msvcrtXX.dll,那么这个程序几乎可以肯定是用多线程MD方式编译的。
四、总结说明
大多数软件都是采用多线程MD方式编译,例如QQ迅雷等等,如果找到他们目录很容易发现上面提到的运行时库。因为这样一来编译出来的文件小,所有运行时库统一,同时也让内存管理简单化,省去了跨模块内存访问带来的各种bug。