MSVC运行时库选项/MD /MDd /MT /MTd
在使用Visual Studio进行C++开发时,有一个编译选项,运行库。包含了/MD, /MDd, /MT, /MTd四个选项。
他们分别的含义可以在MSDN查询到:
选项 | 描述 |
---|---|
/MD | 使用运行时库的多线程DLL版本。编译器会将MSVCRT.lib放入.obj文件中。实际运行时的工作代码在MSVCxxx.DLL中。 |
/MDd | /MD的debug版本 |
/MT | 使用运行时库的多线程,静态编译的版本。编译器会将LIBCMT.lib放入.obj文件中,以便链接器使用 LIBCMT.lib 解析外部符号。 |
/MTd | /MT的debug版本 |
简单来说,/MD是使用运行时库的动态库版本,运行时需要环境有MSVCxxx.dll。而/MT是使用静态编译的运行时库,运行时不需要环境有MSVCxxx.dll。
/MD
输出的.exe或.dll文件更小
运行时需要环境有MVSCRxxx.dll。一般我们可以通过在客户环境安装vc_redist,或在.exe的运行目录拷贝MSVCxxx.dll这两种方式。
如果是通过安装vc_redist的方式,这些相关的DLL将会被安装在系统目录下(%SYSTEMROOT%/System32 or SysWOW64)。这有可能会带来问题,如果其他程序(或系统更新)修改了运行时库,可能会造成现有程序出现异常。
StackOverflow有人说安装的目录已经采用WinSxS的方式,不会有覆盖的问题,这是错误的。
在Visual Studio 2010之后,就取消了这种方式。官方说明在这里。
依赖的其他库也必须是/MD编译
运行时,exe和它依赖的第三方库,以及其他以这种方式玩的进程,都是同一套运行时库。在一定程度上减少了内存的消耗。
/MT
输出的.exe或.dll文件更大
运行时不需要环境有MVSCxxx.dll
依赖的其他库也必须是/MT编译
如果依赖的第三方库是动态库(.dll),则exe在运行时会包含多份运行时库代码,一个是exe自己的,一个是依赖的三方库的。
不正确的使用可能会造成程序Crash
例如在依赖的Dll中malloc,在exe中free。foo.h
1
2
3
4
5
6
7
FOO_EXPORT void* foo();foo.cpp
1
2
3
4
5
6
void* foo()
{
return malloc(10);
}在主工程中调用foo拿到指针,进行free。
main.cpp
1
2
3
4
5
int main()
{
free(foo());
}运行程序会出现下面的结果
为什么会出现
__acrt_first_block == header
的断言错误呢?这是因为/MTd模式下,DLL的运行时库与EXE的运行时库不一样。详细的解析可参考这位大佬的blog。
最佳实践
使用/MD
使用本地部署,即将依赖的运行时库的相关DLL拷贝到发布的应用程序的路径下。
这样做的另一个好处是普通用户也能正常安装应该程序,而不需要管理员权限去写入系统目录。
它的目录结构看起来像这样:
1
2
3
4|--My App\
| |--MyApp.exe
| |--msvc*.dll
| |--...如果为了好看,可以把运行时库单独放到一个子目录内,然后写一个壳子程序或批处理,将子目录添加到环境变量中再启动。
它的目录结构看起来像这样:
1
2
3
4|--My App\
| |--MyApp.exe
| |--Microsoft.VC142.CRT\
| | |--msvc*.dll然后批处理启动
1
2
3@echo off
set PATH=%~dp0Microsoft.VC142.CRT;%PATH%
MyApp.exe或用壳子程序启动,壳子程序大概长下面这样(伪代码)
1
2set_env_to_path(vc_dir);
CreateProcess(...);