日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

带你玩转 Visual Studio——带你跳出坑爹的 Runtime Library 坑

發(fā)布時(shí)間:2023/12/14 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 带你玩转 Visual Studio——带你跳出坑爹的 Runtime Library 坑 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

上一篇文章帶你玩轉(zhuǎn) Visual Studio——帶你理解微軟的預(yù)編譯頭技術(shù)我們了解了微軟的預(yù)編譯頭技術(shù),預(yù)編譯的方式讓我們的工程編譯的更加快速;本篇文章將繼續(xù)介紹微軟的另一項(xiàng)技術(shù),也就是運(yùn)行時(shí)庫(kù) Runtime Library。

在 Windows 下進(jìn)行 C++的開(kāi)發(fā),不可避免的要與 Windows 的底層庫(kù)進(jìn)行交互,然而 VS 下的一項(xiàng)設(shè)置 MT、MTd、MD 和 MDd 卻經(jīng)常讓人搞迷糊,相信不少人都被他坑過(guò),特別是你工程使用了很多第三庫(kù)的時(shí)候,及容易出現(xiàn)各種鏈接問(wèn)題。看一下下面這個(gè)錯(cuò)誤提示: 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)

有多少人被這玩意坑過(guò),被坑過(guò)的請(qǐng)舉腳!哈哈……

既然這里這么容易出問(wèn)題,我們就有必要對(duì)其進(jìn)行深入的了解,知其然且知其所以然才能萬(wàn)事無(wú)懼!

1. 什么是 Runtime Library?

Runtime Library 就是運(yùn)行時(shí)庫(kù),也簡(jiǎn)稱(chēng) CRT(C Run Time Library)。是程序在運(yùn)行時(shí)所需要的庫(kù)文件,通常運(yùn)行時(shí)庫(kù)是以 Lib 或 Dll 形式提供的。

Windows 下C Runtime Library是微軟對(duì) C 標(biāo)準(zhǔn)庫(kù)函數(shù)的實(shí)現(xiàn),這樣每個(gè)程序可以直接使用 C 標(biāo)準(zhǔn)庫(kù)的函數(shù);后來(lái)出現(xiàn)了 C++,于是又在 C Runtime Library 基礎(chǔ)上開(kāi)發(fā)了C++ Runtime Library,實(shí)現(xiàn)了對(duì) C++標(biāo)準(zhǔn)庫(kù)的支持。因此現(xiàn)在 Windows 下的 C/C++ 運(yùn)行時(shí)庫(kù)既包含子 C 標(biāo)準(zhǔn)庫(kù),也包含了 C++ 標(biāo)準(zhǔn)庫(kù)。如果你安裝了 VS2010,在安裝目錄下的 VC\crt\src 下(如我的目錄是 C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\crt\src)有運(yùn)行時(shí)庫(kù)(CRT)的源代碼,這里既有 C 的文件(如 output.c、stdio.h 等),也有 C++ 的文件(如 iostream、string)。

在 C Runtime Library 出現(xiàn)之前,許多程序都使用 C 編寫(xiě),而這些程序都要使用標(biāo)準(zhǔn)的 C 庫(kù),按照以前的方式每一個(gè)程序最終都要拷貝一份標(biāo)準(zhǔn)庫(kù)的實(shí)現(xiàn)到程序中,這樣同一時(shí)刻內(nèi)存中可能有許多份標(biāo)準(zhǔn)庫(kù)的代碼(一個(gè)程序一份),所以微軟出于效率的考慮把標(biāo)準(zhǔn) C 庫(kù)做為動(dòng)態(tài)鏈接來(lái)實(shí)現(xiàn),這樣多個(gè)程序使用 C 標(biāo)準(zhǔn)庫(kù)時(shí)內(nèi)存中就只有一份拷貝了。

確切地說(shuō)運(yùn)行時(shí)庫(kù)指的就是對(duì)這些底層的基礎(chǔ)功能實(shí)現(xiàn)的動(dòng)態(tài)庫(kù)( Dll ),運(yùn)行時(shí)庫(kù)和普通的 Dll 一樣,只有程序用到了它才會(huì)被加載,沒(méi)有程序使用的時(shí)候不會(huì)駐留內(nèi)存的。話雖如此,但有多少系統(tǒng)的東西說(shuō)不定也是用 C 寫(xiě)的,這些東西的存在就使 C 運(yùn)行時(shí)庫(kù)存在于內(nèi)存中了,所以運(yùn)行時(shí)庫(kù)幾乎總是需要的。雖然說(shuō)運(yùn)行時(shí)庫(kù)應(yīng)該是動(dòng)態(tài)庫(kù),但習(xí)慣上我們把與動(dòng)態(tài)運(yùn)行時(shí)庫(kù)相同代碼編譯出來(lái)的靜態(tài)庫(kù)也稱(chēng)為運(yùn)行時(shí)庫(kù),因此 VC++下的運(yùn)行時(shí)庫(kù)有 ML、MLd、MT、MTd、MD、MD 六種(這個(gè)后面會(huì)講)。

1.1 運(yùn)行時(shí)庫(kù)的主要作用

提供 C 標(biāo)準(zhǔn)庫(kù)(如 memcpy、printf、malloc 等)、C++ 標(biāo)準(zhǔn)庫(kù)(STL)的支持。

應(yīng)用程序添加啟動(dòng)函數(shù),啟動(dòng)函數(shù)的主要功能為將要進(jìn)行的程序初始化,對(duì)全局變量進(jìn)行賦初值,加載用戶程序的入口函數(shù)。

不采用寬字符集的控制臺(tái)程序的入口點(diǎn)為 mainCRTStartup(void) 。下面我們以該函數(shù)為例來(lái)分析運(yùn)行時(shí)庫(kù)究竟為我們添加了怎樣的入口程序。這個(gè)函數(shù)在 crt0.c 中被定義,下列的代碼經(jīng)過(guò)了筆者的整理和簡(jiǎn)化:

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();/* 獲得環(huán)境信息 */_aenvptr = (char *) __crtGetEnvironmentStringsA();_setargv(); /* 設(shè)置命令行參數(shù) */_setenvp(); /* 設(shè)置環(huán)境參數(shù) */_cinit(); /* C 數(shù)據(jù)初始化:全局變量初始化,就在這里!*/__initenv = _environ;mainret = main( __argc, __argv, _environ ); /*調(diào)用 main 函數(shù)*/exit( mainret ); }

從以上代碼可知,運(yùn)行庫(kù)在調(diào)用用戶程序的 main 或 WinMain 函數(shù)之前,進(jìn)行了一些初始化工作。初始化完成后,接著才調(diào)用了我們編寫(xiě)的 main 或 WinMain 函數(shù)。只有這樣,我們的 C 語(yǔ)言運(yùn)行時(shí)庫(kù)和應(yīng)用程序才能正常地工作起來(lái)。

除了 crt0.c 外,C 運(yùn)行時(shí)庫(kù)中還包含 wcrt0.c、 wincrt0.c、wwincrt0.c 三個(gè)文件用來(lái)提供初始化函數(shù)。wcrt0.c 是 crt0.c 的寬字符集版,wincrt0.c 中包含 windows 應(yīng)用程序的入口函數(shù),而 wwincrt0.c 則是 wincrt0.c 的寬字符集版。

2. MT、MTd、MD、MDd、( ML、MLd 已廢棄)的區(qū)別與原理

我們可以在 Properties->Configuration Properties->C/C++->Code Generation->Runtime Library 中設(shè)置采用的運(yùn)行時(shí)庫(kù)的類(lèi)型。

在帶你玩轉(zhuǎn) Visual Studio——帶你發(fā)布自己的工程庫(kù)一文中已經(jīng)詳細(xì)講解了靜態(tài)庫(kù) (Lib) 與動(dòng)態(tài)庫(kù) (Dll) 的區(qū)別。我們知道編譯出的靜態(tài)庫(kù)只有一個(gè) ProjectName.lib 文件,而編譯出的動(dòng)態(tài)庫(kù)有兩個(gè)文件:ProjectName.lib+ProjectName.dll,一個(gè)是導(dǎo)入庫(kù),一個(gè)動(dòng)態(tài)庫(kù)。

VC++中有六種 Runtime Library 的類(lèi)型:

你可以在 VS 的安裝目錄下找到這些庫(kù)文件,如我的 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\中分別找到對(duì)應(yīng) 32 位和 64 位的 libcmt.lib、libcmtd.lib、msvcrt.lib、msvcrtd.lib 庫(kù)。libc.lib 和 libcd.lib 由于在 VS2005 就已經(jīng)廢棄了,所以在這肯定找不到。

通過(guò)上面的表格你會(huì)發(fā)現(xiàn),多線程的動(dòng)態(tài)運(yùn)行時(shí)庫(kù)是| msvcrt.lib+msvcrtxx.dll,之所以是 msvcrtxx.dll 是因?yàn)槊恳粋€(gè)版本的 VS 使用的庫(kù)名稱(chēng)還不一樣。而且還不止包含一個(gè)庫(kù),除了主要的 MSVCRT 庫(kù)外,還有 MSVCPRT、MSVCIRT 庫(kù)。它們之間的對(duì)應(yīng)關(guān)系如下:

參考閱讀:https://support.microsoft.com/en-us/kb/154753

在你的 VS 安裝目錄下(如 C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\redist\x64\Microsoft.VC100.CRT),及系統(tǒng)目錄 C:\Windows\System32、C:\Windows\SysWOW64 下都能找到對(duì)應(yīng)的.dll 庫(kù)。

很多的軟件在發(fā)布自己的產(chǎn)品時(shí)也都會(huì)帶上這些 DLL (防止用戶的操作系統(tǒng)沒(méi)有安裝 VS,或在系統(tǒng)目錄下找不到對(duì)應(yīng)的 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). 靜態(tài)鏈接的單線程庫(kù)

靜態(tài)鏈接的單線程庫(kù)只能用于單線程的應(yīng)用程序, C 運(yùn)行時(shí)庫(kù)的目標(biāo)代碼最終被編譯在應(yīng)用程序的二進(jìn)制文件中。通過(guò) /ML 編譯選項(xiàng)可以設(shè)置 Visual C++ 使用靜態(tài)鏈接的單線 程庫(kù)。

(2). 靜態(tài)鏈接的多線程庫(kù)

靜態(tài)鏈接的多線程庫(kù)的目標(biāo)代碼也最終被編譯在應(yīng)用程序的二進(jìn)制文件中,但是它可以在多線程程序中使用。通過(guò) /MT 編譯選項(xiàng)可以設(shè)置 Visual C++ 使用靜態(tài)鏈接的多線程庫(kù)。

該選項(xiàng)生成的可執(zhí)行文件運(yùn)行時(shí)不需要運(yùn)行時(shí)庫(kù) dll 的參加,會(huì)獲得輕微的性能提升,但最終生成的二進(jìn)制代碼因鏈入龐大的運(yùn)行時(shí)庫(kù)實(shí)現(xiàn)而變得非常臃腫。當(dāng)某項(xiàng)目以靜態(tài)鏈接庫(kù)的形式嵌入到多個(gè)項(xiàng)目,則可能造成運(yùn)行時(shí)庫(kù)的內(nèi)存管理有多份,最終將導(dǎo)致致命的“ Invalid Address specified to RtlValidateHeap”問(wèn)題。

(3). 動(dòng)態(tài)鏈接的運(yùn)行時(shí)庫(kù) 動(dòng)態(tài)鏈接的運(yùn)行時(shí)庫(kù)將所有的 C 庫(kù)函數(shù)保存在一個(gè)單獨(dú)的動(dòng)態(tài)鏈接庫(kù) MSVCRTxx.DLL 中, MSVCRTxx.DLL 處理了多線程問(wèn)題。使用 /MD 編譯選項(xiàng)可以設(shè)置 Visual C++ 使用動(dòng)態(tài)。

鏈接時(shí)將按照傳統(tǒng) VC 鏈接 dll 的方式將運(yùn)行時(shí)庫(kù) MSVCRxx.DLL 的導(dǎo)入庫(kù) MSVCRT.lib 鏈接,在運(yùn)行時(shí)要求安裝了相應(yīng)版本的 VC 運(yùn)行時(shí)庫(kù)可再發(fā)行組件包(當(dāng)然把這些運(yùn)行時(shí)庫(kù) dll 放在應(yīng)用程序目錄下也是可以的)。 因/MD 和/MDd 方式不會(huì)將運(yùn)行時(shí)庫(kù)鏈接到可執(zhí)行文件內(nèi)部,可有效減少可執(zhí)行文件尺寸。當(dāng)多項(xiàng)目以 MD 方式運(yùn)作時(shí),其內(nèi)部會(huì)采用同一個(gè)堆,內(nèi)存管理將被簡(jiǎn)化,跨模塊內(nèi)存管理問(wèn)題也能得到緩解。

/MDd 、 /MLd 或 /MTd 選項(xiàng)使用 Debug runtime library ( 調(diào)試版本的運(yùn)行時(shí)刻函數(shù)庫(kù) ) ,與 /MD 、 /ML 或 /MT 分別對(duì)應(yīng)。 Debug 版本的 Runtime Library 包含了調(diào)試信息,并采用了一些保護(hù)機(jī)制以幫助發(fā)現(xiàn)錯(cuò)誤,加強(qiáng)了對(duì)錯(cuò)誤的檢測(cè),因此在運(yùn)行性能方面比不上 Release 版本。

結(jié)論:/MD 和/MDd 將是潮流所趨,/ML 和/MLd 方式請(qǐng)及時(shí)放棄,/MT 和/MTd 在非必要時(shí)最好也不要采用了。

3. 如何避免這種錯(cuò)誤

3.1?/MD 和/MDd 將是潮流所趨,/ML 和/MLd 方式請(qǐng)及時(shí)放棄,/MT 和/MTd 在非必要時(shí)最好也不要采用了。?盡量使用/MD、/MDd 這種方式,除非有某些特殊的需要,如希望編譯出來(lái)的.exe 可執(zhí)行文件不需要依賴運(yùn)行時(shí)庫(kù)的.dll;

3.2?在多工程開(kāi)發(fā)時(shí),所有的工程使用同一種運(yùn)行時(shí)庫(kù)。如 Utils 的 Solution 下有兩個(gè) Project:Utils 和 UsingUtils,UsingUtils 工程要使用 Utils 工程編譯出來(lái)的庫(kù)。如果 Utils 使用了/MDd 的方式,UsingUtils 也要使用/MDd 的方式,否則會(huì)報(bào)鏈接錯(cuò)誤。

如果 Utils 使用 MTd 的方式,而 UsingUtils 使用/MDd 的方式,則會(huì)出現(xiàn)重定義的錯(cuò)誤,如:

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) 這是因?yàn)?Utils 使用 MTd 的方式,包含了 libcmtd.lib 庫(kù);而 UsingUtils 使用/MDd 的方式,要包含 msvcrtd.lib+msvcrtxxd.dll。libcmtd.lib 和 msvcrtd.lib 是用相同代碼編譯的,一個(gè)是靜態(tài)庫(kù),一個(gè)動(dòng)態(tài)庫(kù)的導(dǎo)入庫(kù),同時(shí)包含 libcmtd.lib 和 msvcrtd.lib 肯定就對(duì)相同的函數(shù)進(jìn)行了重復(fù)的定義。

以 Release 方式進(jìn)行編譯時(shí)使用 Release 的庫(kù),使用 Debug 的方式編譯時(shí)使用 Debug 的庫(kù)。如編譯 Release 版本的 UsingUtils 時(shí),要使用 Release 方式編譯出來(lái)的 Utils 庫(kù),編譯 Debug 版本的 UsingUtils 時(shí),要使用 Debug 方式編譯出來(lái)的庫(kù)。

4. 歷史發(fā)展的角度講解運(yùn)行時(shí)庫(kù)

4.1 從操作系統(tǒng)的角度出發(fā)

我們知道操作系統(tǒng)一般是用 C 寫(xiě)的(因?yàn)槟菚r(shí)還沒(méi)有 C++,C 已經(jīng)算是最高級(jí)的計(jì)算機(jī)語(yǔ)言了),不管是 Linux/ Unix 還是 Windows 底層都是大量的 C 代碼。在開(kāi)發(fā)操作系統(tǒng)及相應(yīng)的應(yīng)用程序時(shí),很多的程序都會(huì)用到相同基礎(chǔ)功能的函數(shù)庫(kù)。為了方便開(kāi)發(fā)就把經(jīng)常用到的底層的基礎(chǔ)函數(shù)封閉成庫(kù)(不然你每寫(xiě)一個(gè)程序都要把這基礎(chǔ)功能實(shí)現(xiàn)的源代碼拷貝一份到自己的工程,或自己再實(shí)現(xiàn)一次),于是就有了 C 運(yùn)行時(shí)庫(kù)(C Runtime Library),也就是靜態(tài)庫(kù) libc.lib(Release 版)、libcd.lib(Debug 版)。

因?yàn)樵缙诘牟僮飨到y(tǒng)和程序都相應(yīng)簡(jiǎn)單,用戶的需求也不高,那時(shí)的操作系統(tǒng)還沒(méi)有多任務(wù)、多線程的概念。所以 libc.lib、libcd.lib 當(dāng)然只能支持單線程的程序,而無(wú)法支持多線程的程序,因此這個(gè)運(yùn)行時(shí)庫(kù)叫 Single-Threaded(/ML)的方式。

后來(lái),隨著計(jì)算機(jī)的普及和發(fā)展,計(jì)算機(jī)要完成的任務(wù)越來(lái)越多,人們對(duì)時(shí)間和性能的要求也越來(lái)越高,為滿足這些需求,操作系統(tǒng)就有了多任務(wù)的概念,也有了多線程的技術(shù)。而之前的運(yùn)行時(shí)庫(kù) libc.lib、libcd.lib 只能用于單線程,已經(jīng)無(wú)法滿足很多程序的需要,于是多線程的運(yùn)行時(shí)庫(kù)也就應(yīng)運(yùn)而生,這就是 libcmt.lib、libcmtd.lib,也就是/MT、/MTd 的方式。

/MT、/MTd 解決了多線程的問(wèn)題,但隨著程序的越來(lái)越復(fù)雜,一個(gè)程序可能會(huì)用到多個(gè)其他程序的庫(kù),多個(gè)程序可能會(huì)用到相同的庫(kù),在內(nèi)存中會(huì)保存多份的相同的靜態(tài)庫(kù)。假設(shè) A 程序使用了 C.lib,B 程序也使用了 C.lib,A、B 程序同時(shí)運(yùn)行時(shí),在內(nèi)存中就會(huì)同時(shí)存在兩份 C.lib。

靜態(tài)庫(kù)在內(nèi)存中的呈現(xiàn)方式

為了解決這個(gè)問(wèn)題,就產(chǎn)生了動(dòng)態(tài)庫(kù)的技術(shù)。于是就有了動(dòng)態(tài)的運(yùn)行時(shí)庫(kù) Multi-threaded DLL(/MD)、Multi-threaded DLL Debug(/MDd)。多個(gè)程序使用同一個(gè)動(dòng)態(tài)庫(kù),在內(nèi)存中只會(huì)有一份,效果圖如下

4.2 從語(yǔ)言的角度

我們都知道,先有了 C 語(yǔ)言,后來(lái)才在 C 的基礎(chǔ)上發(fā)展出了 C++ 語(yǔ)言,而 C++ 又對(duì) C 兼容,相當(dāng)是 C 的一個(gè)超集。 一開(kāi)始的運(yùn)行時(shí)庫(kù),只是 C 的運(yùn)行時(shí)庫(kù)(C Runtime Library),包含了對(duì) C 標(biāo)準(zhǔn)函數(shù)的實(shí)現(xiàn)。后來(lái)隨著 C++的產(chǎn)生,又把 C++ 程序運(yùn)行時(shí)要用的底層基礎(chǔ)庫(kù)加了進(jìn)去,就有了 C/C++的運(yùn)行時(shí)庫(kù)(C/C++ Runtime Library),這時(shí)的運(yùn)行時(shí)庫(kù)既包含了標(biāo)準(zhǔn) C 函數(shù)的實(shí)現(xiàn),也包含了 C++ STL 的實(shí)現(xiàn)。

?

此文章源自于【https://wiki.jikexueyuan.com/project/visual-studio/13.html】

總結(jié)

以上是生活随笔為你收集整理的带你玩转 Visual Studio——带你跳出坑爹的 Runtime Library 坑的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。