<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??一站式輕松地調用各大LLM模型接口,支持GPT4、智譜、豆包、星火、月之暗面及文生圖、文生視頻 廣告
                上一篇文章[帶你玩轉Visual Studio——帶你理解微軟的預編譯頭技術](http://blog.csdn.net/luoweifu/article/details/49010627)我們了解了微軟的預編譯頭技術,預編譯的方式讓我們的工程編譯的更加快速;本篇文章將繼續介紹微軟的另一項技術,也就是運行時庫Runtime Library。 * * * 在Windows下進行C++的開發,不可避免的要與Windows的底層庫進行交互,然而VS下的一項設置MT、MTd、MD和MDd卻經常讓人搞迷糊,相信不少人都被他坑過,特別是你工程使用了很多第三庫的時候,及容易出現各種鏈接問題。看一下下面這個錯誤提示:? LIBCMT.lib(_file.obj) : error LNK2005: ___initstdio already defined in libc.lib(_file.obj)? LIBCMT.lib(_file.obj) : error LNK2005: ___endstdio already defined in libc.lib(_file.obj) 有多少人被這玩意坑過,被坑過的請舉腳!哈哈…… 既然這里這么容易出問題,我們就有必要對其進行深入的了解,**知其然且知其所以然才能萬事無懼!** # 1\. 什么是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++標準庫的支持。因此現在Windows下的C/C++運行時庫既包含子C標準庫,也包含了C++標準庫。如果你安裝了VS2010,在安裝目錄下的VC\crt\src下(如我的目錄是C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src)有運行時庫(CRT)的源代碼,這里既有C的文件(如output.c、stdio.h等),也有C++的文件(如iostream、string)。 在C Runtime Library出現之前,許多程序都使用C編寫,而這些程序都要使用標準的C庫,按照以前的方式每一個程序最終都要拷貝一份標準庫的實現到程序中,這樣同一時刻內存中可能有許多份標準庫的代碼(一個程序一份),所以微軟出于效率的考慮把標準C庫做為動態鏈接來實現,這樣多個程序使用C標準庫時內存中就只有一份拷貝了。 確切地說運行時庫指的就是對這些底層的基礎功能實現的動態庫(Dll),運行時庫和普通的Dll一樣,只有程序用到了它才會被加載,沒有程序使用的時候不會駐留內存的。話雖如此,但有多少系統的東西說不定也是用C寫的,這些東西的存在就使C運行時庫存在于內存中了,所以運行時庫幾乎總是需要的。雖然說運行時庫應該是動態庫,但習慣上我們把與動態運行時庫相同代碼編譯出來的靜態庫也稱為運行時庫,因此VC++下的運行時庫有ML、MLd、MT、MTd、MD、MD六種(這個后面會講)。 ## 1.1 運行時庫的主要作用 1. 提供C標準庫(如memcpy、printf、malloc等)、C++標準庫(STL)的支持。 2. 應用程序添加啟動函數,啟動函數的主要功能為將要進行的程序初始化,對全局變量進行賦初值,加載用戶程序的入口函數。 不采用寬字符集的控制臺程序的入口點為mainCRTStartup(void)。下面我們以該函數為例來分析運行時庫究竟為我們添加了怎樣的入口程序。這個函數在crt0.c中被定義,下列的代碼經過了筆者的整理和簡化: ~~~ void mainCRTStartup(void) {  int mainret;  /*獲得WIN32完整的版本信息*/  _osver = GetVersion();  _winminor = (_osver >> 8) & 0x00FF ;  _winmajor = _osver & 0x00FF ;  _winver = (_winmajor << 8) + _winminor;  _osver = (_osver >> 16) & 0x00FFFF ;  _ioinit(); /* initialize lowio */  /* 獲得命令行信息 */  _acmdln = (char *) GetCommandLineA();  /* 獲得環境信息 */  _aenvptr = (char *) __crtGetEnvironmentStringsA();  _setargv(); /* 設置命令行參數 */  _setenvp(); /* 設置環境參數 */  _cinit(); /* C數據初始化:全局變量初始化,就在這里!*/  __initenv = _environ;  mainret = main( __argc, __argv, _environ ); /*調用main函數*/  exit( mainret ); } ~~~ 從以上代碼可知,運行庫在調用用戶程序的main或WinMain函數之前,進行了一些初始化工作。初始化完成后,接著才調用了我們編寫的main或WinMain函數。只有這樣,我們的C語言運行時庫和應用程序才能正常地工作起來。 除了crt0.c外,C運行時庫中還包含wcrt0.c、 wincrt0.c、wwincrt0.c三個文件用來提供初始化函數。wcrt0.c是crt0.c的寬字符集版,wincrt0.c中包含 windows應用程序的入口函數,而wwincrt0.c則是wincrt0.c的寬字符集版。 # 2\. MT、MTd、MD、MDd、(ML、MLd 已廢棄)的區別與原理 我們可以在Properties->Configuration Properties->C/C++->Code Generation->Runtime Library中設置采用的運行時庫的類型。? ![](https://box.kancloud.cn/2016-01-19_569dd03baca12.jpg)? Runtime Library 在[帶你玩轉Visual Studio——帶你發布自己的工程庫](http://blog.csdn.net/luoweifu/article/details/48895765)一文中已經詳細講解了靜態庫(Lib)與動態庫(Dll)的區別。我們知道編譯出的靜態庫只有一個ProjectName.lib文件,而編譯出的動態庫有兩個文件:ProjectName.lib+ProjectName.dll,一個是導入庫,一個動態庫。 **VC++中有六種Runtime Library的類型:** | 類型 | 簡稱 | 含義 | 對應的庫名稱 | 備注 | | --- | --- | --- | --- | --- | | Single-Threaded | /ML | Release版的單線程靜態庫 | libc.lib | VS2003以后被廢棄 | | Single-Threaded Debug | /MLd | Debug版的單線程靜態庫 | libcd.lib | VS2003以后被廢棄 | | Multi-threaded | /MT | Release版的多線程靜態庫 | libcmt.lib | | | Multi-threaded Debug | /MTd | Debug版的多線程靜態庫 | libcmtd.lib | | | Multi-threaded DLL | /MD | Release版的多線程動態庫 | msvcrt.lib+msvcrtxx.dll | | | Multi-threaded DLL Debug | MDd | Debug版的多線程動態庫 | msvcrtd.lib+msvcrtxxd.dll | | 你可以在VS的安裝目錄下找到這些庫文件,如我的VS2010安裝在C:\Program Files (x86)\Microsoft Visual Studio 10.0,則可以在C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib\和C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\lib\amd64\中分別找到對應32位和64位的libcmt.lib、libcmtd.lib、msvcrt.lib、msvcrtd.lib庫。libc.lib和libcd.lib由于在VS2005就已經廢棄了,所以在這肯定找不到。 通過上面的表格你會發現,多線程的動態運行時庫是|msvcrt.lib+msvcrtxx.dll,之所以是msvcrtxx.dll是因為每一個版本的VS使用的庫名稱還不一樣。而且還不止包含一個庫,除了主要的MSVCRT庫外,還有MSVCPRT、MSVCIRT庫。它們之間的對應關系如下: | 導入庫 | MSVCRT.LIB | MSVCRTD.LIB | MSVCPRT.LIB | MSVCPRTD.LIB | MSVCIRT.LIB | MSVCIRTD.LIB | | --- | --- | --- | --- | --- | --- | --- | | Visual C++ 5.0 | MSVCRT.DLL | MSVCRTD.DLL | MSVCP5.DLL | MSVCP5D.DLL | MSVCIRT.DLL | MSVCIRTD.DLL | | Visual C++ 6.0 | MSVCRT.DLL | MSVCRTD.DLL | MSVCP6.DLL | MSVCP6D.DLL | MSVCIRT.DLL | MSVCIRTD.DLL | | Visual C++ .NET 2002 | MSVCR70.DLL | MSVCR70D.DLL | MSVCP70.DLL | MSVCP70D.DLL | | | | Visual C++ .NET 2003 | MSVCR71.DLL | MSVCR71D.DLL | MSVCP71.DLL | MSVCP71D.DLL | | | | Visual C++ 2005 | MSVCR80.DLL | MSVCR80D.DLL | MSVCP80.DLL | MSVCP80D.DLL | | | | Visual C++ 2008 | MSVCR90.DLL | MSVCR90.DLL | MSVCP90.DLL | MSVCP90D.DLL | | | | Visual C++ 2010 | MSVCR100.DLL | MSVCR100D.DLL | MSVCP100.DLL | MSVCP100D.DLL | | | 參考閱讀:[https://support.microsoft.com/en-us/kb/154753](https://support.microsoft.com/en-us/kb/154753) 在你的VS安裝目錄下(如C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\redist\x64\Microsoft.VC100.CRT),及系統目錄C:\Windows\System32、C:\Windows\SysWOW64下都能找到對應的.dll庫。 很多的軟件在發布自己的產品時也都會帶上這些DLL(防止用戶的操作系統沒有安裝VS,或在系統目錄下找不到對應的DLL),如我電腦上的百度影音安裝目錄下就有MSVCR71.DLL(C:\Program Files (x86)\baidu\BaiduPlayer\4.1.2.286\MSVCR71.DLL),WPS的安裝目錄下有msvcr100.dll(C:\Program Files (x86)\WPS Office\9.1.0.5132\wtoolex\msvcr100.dll)和msvcp100.dll(C:\Program Files (x86)\WPS Office\9.1.0.5132\wtoolex\msvcp100.dll) (1). 靜態鏈接的單線程庫? 靜態鏈接的單線程庫只能用于單線程的應用程序, C 運行時庫的目標代碼最終被編譯在應用程序的二進制文件中。通過 /ML 編譯選項可以設置 Visual C++ 使用靜態鏈接的單線? 程庫。? (2). 靜態鏈接的多線程庫? 靜態鏈接的多線程庫的目標代碼也最終被編譯在應用程序的二進制文件中,但是它可以在多線程程序中使用。通過 /MT 編譯選項可以設置 Visual C++ 使用靜態鏈接的多線程庫。? 該選項生成的可執行文件運行時不需要運行時庫dll的參加,會獲得輕微的性能提升,但最終生成的二進制代碼因鏈入龐大的運行時庫實現而變得非常臃腫。當某項目以靜態鏈接庫的形式嵌入到多個項目,則可能造成運行時庫的內存管理有多份,最終將導致致命的“Invalid Address specified to RtlValidateHeap”問題。? (3). 動態鏈接的運行時庫? 動態鏈接的運行時庫將所有的 C 庫函數保存在一個單獨的動態鏈接庫 MSVCRTxx.DLL 中, MSVCRTxx.DLL 處理了多線程問題。使用 /MD 編譯選項可以設置 Visual C++ 使用動態。? 鏈接時將按照傳統VC鏈接dll的方式將運行時庫MSVCRxx.DLL的導入庫MSVCRT.lib鏈接,在運行時要求安裝了相應版本的VC運行時庫可再發行組件包(當然把這些運行時庫dll放在應用程序目錄下也是可以的)。 因/MD和/MDd方式不會將運行時庫鏈接到可執行文件內部,可有效減少可執行文件尺寸。當多項目以MD方式運作時,其內部會采用同一個堆,內存管理將被簡化,跨模塊內存管理問題也能得到緩解。 /MDd 、 /MLd 或 /MTd 選項使用 Debug runtime library( 調試版本的運行時刻函數庫 ) ,與 /MD 、 /ML 或 /MT 分別對應。 Debug 版本的 Runtime Library 包含了調試信息,并采用了一些保護機制以幫助發現錯誤,加強了對錯誤的檢測,因此在運行性能方面比不上 Release 版本。 **結論:/MD和/MDd將是潮流所趨,/ML和/MLd方式請及時放棄,/MT和/MTd在非必要時最好也不要采用了。** # 3\. 如何避免這種錯誤 3.1./MD和/MDd將是潮流所趨,/ML和/MLd方式請及時放棄,/MT和/MTd在非必要時最好也不要采用了。盡量使用/MD、/MDd這種方式,除非有某些特殊的需要,如希望編譯出來的.exe可執行文件不需要依賴運行時庫的.dll;? 2.在多工程開發時,所有的工程使用同一種運行時庫。如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肯定就對相同的函數進行了重復的定義。 1. 以Release方式進行編譯時使用Release的庫,使用Debug的方式編譯時使用Debug的庫。如編譯Release版本的UsingUtils時,要使用Release方式編譯出來的Utils庫,編譯Debug版本的UsingUtils時,要使用Debug方式編譯出來的庫。? # 4\. 歷史發展的角度講解運行時庫 ## 4.1 從操作系統的角度出發 我們知道操作系統一般是用C寫的(因為那時還沒有C++,C已經算是最高級的計算機語言了),不管是Linux/Unix還是Windows底層都是大量的C代碼。在開發操作系統及相應的應用程序時,很多的程序都會用到相同基礎功能的函數庫。為了方便開發就把經常用到的底層的基礎函數封閉成庫(不然你每寫一個程序都要把這基礎功能實現的源代碼拷貝一份到自己的工程,或自己再實現一次),于是就有了**C運行時庫(C Runtime Library)**,也就是靜態庫libc.lib(Release版)、libcd.lib(Debug版)。 因為早期的操作系統和程序都相應簡單,用戶的需求也不高,那時的操作系統還沒有多任務、多線程的概念。所以libc.lib、libcd.lib當然只能支持單線程的程序,而無法支持多線程的程序,因此這個運行時庫叫Single-Threaded(/ML)的方式。 后來,隨著計算機的普及和發展,計算機要完成的任務越來越多,人們對時間和性能的要求也越來越高,為滿足這些需求,操作系統就有了多任務的概念,也有了多線程的技術。而之前的運行時庫libc.lib、libcd.lib只能用于單線程,已經無法滿足很多程序的需要,于是多線程的運行時庫也就應運而生,這就是libcmt.lib、libcmtd.lib,也就是/MT、/MTd的方式。 /MT、/MTd解決了多線程的問題,但隨著程序的越來越復雜,一個程序可能會用到多個其他程序的庫,多個程序可能會用到相同的庫,在內存中會保存多份的相同的靜態庫。假設A程序使用了C.lib,B程序也使用了C.lib,A、B程序同時運行時,在內存中就會同時存在兩份C.lib。? ![](https://box.kancloud.cn/2016-01-19_569dd03bbe90b.jpg)? 靜態庫在內存中的呈現方式 為了解決這個問題,就產生了動態庫的技術。于是就有了動態的運行時庫Multi-threaded DLL(/MD)、Multi-threaded DLL Debug(/MDd)。多個程序使用同一個動態庫,在內存中只會有一份,效果圖如下? ![](https://box.kancloud.cn/2016-01-19_569dd03bcf373.jpg)? 動態庫在內存中的呈現方式 ## 4.2 從語言的角度 我們都知道,先有了C語言,后來才在C的基礎上發展出了C++語言,而C++又對C兼容,相當是C的一個超集。? 一開始的運行時庫,只是C的運行時庫(C Runtime Library),包含了對C標準函數的實現。后來隨著C++的產生,又把C++程序運行時要用的底層基礎庫加了進去,就有了C/C++的運行時庫(C/C++ Runtime Library),這時的運行時庫既包含了標準C函數的實現,也包含了C++ STL的實現。 上一篇回顧:? [帶你玩轉Visual Studio——帶你理解微軟的預編譯頭技術](http://blog.csdn.net/luoweifu/article/details/49010627) 下一篇要講述的內容:? 帶你玩轉Visual Studio——帶你理解多字節編碼與Unicode碼
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看