一文带你弄懂Visual Studio:运行时库及MT/MTDMD/MDD

Posted CodeBowl

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一文带你弄懂Visual Studio:运行时库及MT/MTDMD/MDD相关的知识,希望对你有一定的参考价值。

引子

在做vs开发的时候,一定会遇到一个问题,将在你自己电脑上编译的程序,拿到其他电脑上运行,经常会产生无法运行的情况,特别是你将你的程序拷贝到xp系统上时,这是为什么呢?
一般遇到这个问题,大家第一步可能就是上网搜索,希望可以看到这篇帖子。

这一般是由于另一台机器上面没有安装响应的运行时库导致的,那么这个与编译选项MT、MTd、MD、MDd有什么关系呢?

什么是Runtime Library?

Runtime Library就是运行时库,也简称CRT(C Run Time Library)。是程序在运行时所需要的库文件,通常运行时库是以Lib或Dll形式提供的。
Windows下C Runtime Library是微软对C标准库函数的实现,这样每个程序可以直接使用C标准库的函数;后来出现了C++,于是又在C Runtime Library基础上开发了C++ Runtime Library,实现了对C++标准库的支持。
在C Runtime Library出现之前,许多程序都使用C编写,而这些程序都要使用标准的C库,按照以前的方式每一个程序最终都要拷贝一份标准库的实现到程序中,这样同一时刻内存中可能有许多份标准库的代码(一个程序一份),所以微软出于效率的考虑把标准C库做为动态链接来实现,这样多个程序使用C标准库时内存中就只有一份拷贝了。
确切地说运行时库指的就是对这些底层的基础功能实现的动态库(Dll),运行时库和普通的Dll一样,只有程序用到了它才会被加载,没有程序使用的时候不会驻留内存的。话虽如此,但有多少系统的东西说不定也是用C写的,这些东西的存在就使C运行时库存在于内存中了,所以运行时库几乎总是需要的。虽然说运行时库应该是动态库,但习惯上我们把与动态运行时库相同代码编译出来的静态库也称为运行时库,因此VC++下的运行时库有MT、MTd、MD、MD。

Runtime Library和运行库 MT MTD MD MDD的关系

这是msdn上面的解释:

MT:mutithread,多线程库,编译器会从运行时库里面选择多线程静态连接库来解释程序中的代码,即连接LIBCMT.lib库
MTd:mutithread+debug,多线程调试版,连接LIBMITD.lib库
MD:MT+DLL,多线程动态库,连接MSVCRT.lib库,这是个导入库,对应动态库为MSVCRT.dll MDd:
MT+DLL+debug,多线程动态调试库,连接MSVCRTD.lib库,对应动态库为MSVCRTD.dll

开发多线程程序时,需要选择MT、MTd、MD、MDd其中的一个。

  • 对于MT/MTd,由于连接运行时库是LIBCMT.lib/LIBCMTD.lib,这两个库是静态库,所以此种方式编译的程序,移到另一台机器上面也可以正常运行;所以这种方式,不会产生缺少动态库的报错。
  • 但是对于MD/MDd,连接的是动态库,所以如果另一台机器上没有MSVCRT.dll/MSVCRTD.dll时,就提示缺少动态库这样的错误。

注意!!!
曾经犯这样的错误,以为以MT/MTd方式编译,程序对所有的库都是静态链接的,其实错了,它只能决定运行时库是动态链接还是静态链接,对用户自己写的库或其他第三方库,其连接方式取决于代码(显示连接动态库Loadlibrary)或所提供的lib文件(为导入库还是静态库),移动程序到别的机器上时,还是要带上所需要的动态库的。
多线程的动态运行时库是|msvcrt.lib+msvcrtxx.dll,之所以是msvcrtxx.dll是因为每一个版本的VS使用的库名称还不一样。而且还不止包含一个库,除了主要的MSVCRT库外,还有MSVCPRT、MSVCIRT库。它们之间的对应关系如下:

在你的VS安装目录下(如C:\\Program Files (x86)\\Microsoft Visual Studio 12.0\\VC\\redist\\x64\\Microsoft.VC120.CRT),及系统目录C:\\Windows\\System32、C:\\Windows\\SysWOW64下都能找到对应的.dll库。

很多的软件在发布自己的产品时也都会带上这些DLL(防止用户的操作系统没有安装VS,或在系统目录下找不到对应的DLL)。

静态链接的多线程库

静态链接的多线程库的目标代码也最终被编译在应用程序的二进制文件中,但是它可以在多线程程序中使用。通过 /MT 编译选项可以设置 Visual C++ 使用静态链接的多线程库。
该选项生成的可执行文件运行时不需要运行时库dll的参加,会获得轻微的性能提升,但最终生成的二进制代码因链入庞大的运行时库实现而变得非常臃肿。当某项目以静态链接库的形式嵌入到多个项目,则可能造成运行时库的内存管理有多份,最终将导致致命的“Invalid Address specified to RtlValidateHeap”问题。

动态链接的运行时库

动态链接的运行时库将所有的 C 库函数保存在一个单独的动态链接库 MSVCRTxx.DLL 中, MSVCRTxx.DLL 处理了多线程问题。使用 /MD 编译选项可以设置 Visual C++ 使用动态。
链接时将按照传统VC链接dll的方式将运行时库MSVCRxx.DLL的导入库MSVCRT.lib链接,在运行时要求安装了相应版本的VC运行时库可再发行组件包(当然把这些运行时库dll放在应用程序目录下也是可以的)。 因/MD和/MDd方式不会将运行时库链接到可执行文件内部,可有效减少可执行文件尺寸。当多项目以MD方式运作时,其内部会采用同一个堆,内存管理将被简化,跨模块内存管理问题也能得到缓解。

MDD、MTD和MD、MT

/MDd 或 /MTd 选项使用 Debug runtime library( 调试版本的运行时刻函数库 ) ,与 /MD 或 /MT 分别对应。 Debug 版本的 Runtime Library 包含了调试信息,并采用了一些保护机制以帮助发现错误,加强了对错误的检测,因此在运行性能方面比不上 Release 版本。

开发时注意事项

1.在多工程开发时,所有的工程使用同一种运行时库。

在多工程开发时,所有的工程使用同一种运行时库。如Utils的Solution下有两个Project:Utils和UsingUtils,UsingUtils工程要使用Utils工程编译出来的库。如果Utils使用了/MDd的方式,UsingUtils也要使用/MDd的方式,否则会报链接错误。
如果Utils使用MTd的方式,而UsingUtils使用/MDd的方式,则会出现重定义的错误,如:

1>LIBCMTD.lib(setlocal.obj) : error LNK2005: __configthreadlocale already defined in MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(dbgheap.obj) : error LNK2005: __free_dbg already defined in MSVCRTD.lib(MSVCR100D.dll)
1>LIBCMTD.lib(dbgheap.obj) : error LNK2005: __CrtSetCheckCount already defined in MSVCRTD.lib(MSVCR100D.dll)

这是因为Utils使用MTd的方式,包含了libcmtd.lib库;而UsingUtils使用/MDd的方式,要包含msvcrtd.lib+msvcrtxxd.dll。libcmtd.lib和msvcrtd.lib是用相同代码编译的,一个是静态库,一个动态库的导入库,同时包含libcmtd.lib和msvcrtd.lib肯定就对相同的函数进行了重复的定义。

以Release方式进行编译时使用Release的库,使用Debug的方式编译时使用Debug的库。如编译Release版本的UsingUtils时,要使用Release方式编译出来的Utils库,编译Debug版本的UsingUtils时,要使用Debug方式编译出来的库。

2.建议使用MD、MDD

/MT、/MTd解决了多线程的问题,但随着程序的越来越复杂,一个程序可能会用到多个其他程序的库,多个程序可能会用到相同的库,在内存中会保存多份的相同的静态库。假设A程序使用了C.lib,B程序也使用了C.lib,A、B程序同时运行时,在内存中就会同时存在两份C.lib。
(图源:luoweifu是一个文章写的很棒的大佬,推荐!! https://sunlogging.blog.csdn.net/article/details/49055933)

为了解决这个问题,就产生了动态库的技术。于是就有了动态的运行时库Multi-threaded DLL(/MD)、Multi-threaded DLL Debug(/MDd)。多个程序使用同一个动态库,在内存中只会有一份,效果图如下:

参考资料

《visual studio运行时库MT、MTd、MD、MDd的研究》https://blog.csdn.net/ybxuwei/article/details/9095067
《带你玩转Visual Studio——带你跳出坑爹的Runtime Library坑》https://sunlogging.blog.csdn.net/article/details/49055933

靓仔有话说

vs是我接触的第一个也是用的最多的IDE,也被称为最强IDE,一般用在Windows开发上,之后靓仔将长期更新此系列,和大家一起学习使用visual studio!

以上是关于一文带你弄懂Visual Studio:运行时库及MT/MTDMD/MDD的主要内容,如果未能解决你的问题,请参考以下文章

Visual Studio 2010 的 Visual C++ 2012 运行时库先决条件

Visual Studio 2010 运行时库

Visual Studio 2015:在没有运行时库的情况下编译 C/C++

Visual Studio 2010 SP1 安装项目不安装 Visual C++ 2010 运行时库的 SP1

Microsoft visual studio C 运行时库 在 xx.exe中检测到一个错误

如何在调试构建期间使 Visual Studio (2019/2022) 链接到正常的运行时库?