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版本。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM