CRT
當(dāng)C Runtime函數(shù)庫于20世紀(jì)70年代產(chǎn)生出來時,PC的內(nèi)存容量還很小,多任務(wù)是個新奇觀念,更別提什么多線程了。因此以當(dāng)時產(chǎn)品為基礎(chǔ)所演化的C Runtime函數(shù)庫在多線程(multithreaded)的表現(xiàn)上有嚴(yán)重問題,無法被多線程程序使用。
利用各種同步機制(synchronous mechanism)如critical section、mutex、semaphore、event,可以重新開發(fā)一套支持多線程的runtime函數(shù)庫。問題是,加上這樣的能力,可能導(dǎo)致程序代碼大小和執(zhí)行效率都遭受不良波及──即使你只激活了一個線程。
Visual C++ 的折衷方案是提供兩種版本的C Runtime函數(shù)庫。一種版本給單線程程序使用,一種版本給多線程程序使用。多線程版本的重大改變是:第一,變量如errno現(xiàn)在變成每個線程各擁有一個。第二,多線程版中的數(shù)據(jù)結(jié)構(gòu)以同步機制加以保護。
Visual C++ 一共有六個C Runtime函數(shù)庫產(chǎn)品供你選擇:
◆?? Single-Threaded(static)??????????? libc.lib???????????? 898,826
◆?? Multithreaded(static)????????????? libcmt.lib?????????? 951,142
◆?? Multithreaded DLL??????????????????? msvcrt.lib?????????? 5,510,000
◆?? Debug Single-Threaded(static)????? libcd.lib??????????? 2,374,542
◆?? Debug Multithreaded(static)??????? libcmtd.lib????????? 2,949,190
◆?? Debug Multithreaded DLL????????????? msvcrtd.lib????????? 803,418
Visual C++ 編譯器提供下列選項,讓我們決定使用哪一個C Runtime函數(shù)庫:
◆?? /ML????????? Single-Threaded(static)
◆?? /MT????????? Multithreaded(static)
◆?? /MD????????? Multithreaded DLL(dynamic import library)
◆?? /MLd???????? Debug Single-Threaded(static)
◆?? /MTd???????? Debug Multithreaded(static)
◆?? /MDd???????? Debug Multithreaded DLL(dynamic import library)
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/chuajiang/archive/2007/06/21/1660475.aspx
?
MSVC CRT
MSVC CRT是微軟發(fā)布的c運行時,一直伴隨著不同版本的Visual C++發(fā)布的MSVC CRT(Microsoft Visual C++ C Runtime)倒看過去更加有序一些。從1992年最初的Visual C++ 1.0版開始,一直到現(xiàn)在的Visual C++ 9.0(又叫做Visual C++ 2008),MSVC CRT也從1.0版發(fā)展到了9.0版。
同一個版本的MSVC CRT根據(jù)不同的屬性提供了多種子版本,以供不同需求的開發(fā)者使用。按照靜態(tài)/動態(tài)鏈接,可以分為靜態(tài)版和動態(tài)版;按照單線程/多線程,可以分為單線程版和多線程版;按照調(diào)試/發(fā)布,可分為調(diào)試版和發(fā)布版;按照是否支持C++分為純C運行庫版和支持C++版;按照是否支持托管代碼分為支持本地代碼/托管代碼和純托管代碼版。這些屬性很多時候是相互正交的,也就是說它們之間可以相互組合。比如可以有靜態(tài)單線程純C純本地代碼調(diào)試版;也可以有動態(tài)的多線程純C純本地代碼發(fā)布版等。但有些組合是沒有的,比如動態(tài)鏈接版本的CRT是沒有單線程的,所有的動態(tài)鏈接CRT都是多線程安全的。
這樣的不同組合將會出現(xiàn)非常多的子版本,于是微軟提供了一套運行庫的命名方法。這個命名方法是這樣的,靜態(tài)版和動態(tài)版完全不同。靜態(tài)版的CRT位于MSVC安裝目錄下的lib/,比如Visual C++ 2008的靜態(tài)庫路徑為“Program Files/Microsoft Visual Studio 9.0/VC/lib”,它們的命名規(guī)則為:
libc [p] [mt] [d] .lib
l?????????? p 表示 C Plusplus,即C++標(biāo)準(zhǔn)庫。
l?????????? mt表示 Multi-Thread,即表示支持多線程。
l?????????? d 表示 Debug,即表示調(diào)試版本。
比如靜態(tài)的非C++的多線程版CRT的文件名為libcmtd.lib。動態(tài)版的CRT的每個版本一般有兩個相對應(yīng)的文件,一個用于鏈接的.lib文件,一個用于運行時用的.dll動態(tài)鏈接庫。它們的命名方式與靜態(tài)版的CRT非常類似,稍微有所不同的是,CRT的動態(tài)鏈接庫DLL文件名中會包含版本號。比如Visual C++ 2005的多線程、動態(tài)鏈接版的DLL文件名為msvcr90.dll(Visual C++ 2005的內(nèi)部版本號為8.0)。表11-1列舉了一些最常見的MSVC CRT版本(以Visual C++ 2005為例)。
表11-1
?
| 文件名 | 相關(guān)的DLL | 屬性 | 編譯器選項 | 預(yù)編譯宏 |
| libcmt.lib | 無 | 多線程,靜態(tài)鏈接 | /MT | _MT |
| msvcrt.lib | msvcr80.dll | 多線程,動態(tài)鏈接 | /MD | _MT, _DLL |
| libcmtd.lib | 無 | 多線程,靜態(tài)鏈接,調(diào)試 | /MTd | _DEBUG, _MT |
| msvcrtd.lib | msvcr90d.dll | 多線程,動態(tài)鏈接,調(diào)試 | /MDd | _DEBUG, _MT, _DLL |
| msvcmrt.lib | msvcm90.dll | 托管/本地混合代碼 | /clr | ? |
| msvcurt.lib | msvcm90.dll | 純托管代碼 | /clr:pure | ? |
?
自從Visual C++ 2005(MSVC 8.0)以后,MSVC不再提供靜態(tài)鏈接單線程版的運行庫(LIBC.lib、LIBCD.lib),因為據(jù)微軟聲稱,經(jīng)過改進后的新的多線程版的C運行庫在單線程的模式下運行速度已經(jīng)接近單線程版的運行庫,于是沒有必要再額外提供一個只支持單線程的CRT版本。
默認情況下,如果在編譯鏈接時不指定鏈接哪個CRT,編譯器會默認選擇LIBCMT.LIB,即靜態(tài)多線程CRT,Visual C++ 2005之前的版本會選擇LIBC.LIB,即靜態(tài)單線程版本。關(guān)于CRT的多線程和單線程的問題,我們在后面的章節(jié)還會再深入分析。
除了使用編譯命令行的選項之外,在Visual C++工程屬性中也可以設(shè)置相關(guān)選項。如圖11-9所示。
?
?
圖11-9? Visual C++ 2003 .NET工程屬性的截圖
我們可以從圖11-9中看到,除了多線程庫以外,還有單線程靜態(tài)/ML、單線程靜態(tài)調(diào)試/MLd的選項。
C++ CRT
表11-1中的所有CRT都是指C語言的標(biāo)準(zhǔn)庫,MSVC還提供了相應(yīng)的C++標(biāo)準(zhǔn)庫。如果你的程序是使用C++編寫的,那么就需要額外鏈接相應(yīng)的C++標(biāo)準(zhǔn)庫。這里“額外”的意思是,如表11-2所列的C++標(biāo)準(zhǔn)庫里面包含的僅僅是C++的內(nèi)容,比如iostream、string、map等,不包含C的標(biāo)準(zhǔn)庫。
表11-2
| 文件名 | 相應(yīng)DLL | 屬性 | 編譯選項 | 宏定義 |
| LIBCPMT.LIB | 無 | 多線程,靜態(tài)鏈接 | /MT | _MT |
| MSVCPRT.LIB | MSVCP90.dll | 多線程,動態(tài)鏈接 | /MD | _MT, _DLL |
| LIBCPMTD.LIB | 無 | 多線程,靜態(tài)鏈接,調(diào)試 | /MTd | _DEBUG, _MT |
| MSVCPRTD.LIB | MSVCP90D.dll | 多線程,動態(tài)鏈接,調(diào)試 | /MDd | _DEBUG, _MT, _DLL |
?
當(dāng)你在程序里包含了某個C++標(biāo)準(zhǔn)庫的頭文件時,MSVC編譯器就認為該源代碼文件是一個C++源代碼程序,它會在編譯時根據(jù)編譯選項,在目標(biāo)文件的“.drectve”段(還記得第2章中的DIRECTIVE吧?)相應(yīng)的C++標(biāo)準(zhǔn)庫鏈接信息。比如我們用C++寫一個“Hello World”程序:
// hello.cpp
#include <iostream>
int main()
{
??? std::cout << "Hello world" << std::endl;
??? return 0;
}
然后將它編譯成目標(biāo)文件,并查看它的“.drectve”段的信息:
cl /c hello.cpp
dumpbin /DIRECTIVES hello.obj
Microsoft (R) COFF/PE Dumper Version 9.00.21022.08
Copyright (C) Microsoft Corporation.? All rights reserved.
Dump of file msvcprt.obj
File Type: COFF OBJECT
?? Linker Directives
?? -----------------
?? /DEFAULTLIB:"libcpmt"
?? /DEFAULTLIB:"LIBCMT"
?? /DEFAULTLIB:"OLDNAMES"
cl /c /MDd hello.cpp
dumpbin /DIRECTIVES hello.obj
Microsoft (R) COFF/PE Dumper Version 9.00.21022.08
Copyright (C) Microsoft Corporation.? All rights reserved.
Dump of file msvcprt.obj
File Type: COFF OBJECT
?? Linker Directives
?? -----------------
?? /manifestdependency:"type='win32'
?? name='Microsoft.VC90.DebugCRT'
?? version='9.0.21022.8'
?? processorArchitecture='x86'
?? publicKeyToken='1fc8b3b9a1e18e3b'"
?? /DEFAULTLIB:"msvcprtd"
?? /manifestdependency:"type='win32'
?? name='Microsoft.VC90.DebugCRT'
?? version='9.0.21022.8'
?? processorArchitecture='x86'
?? publicKeyToken='1fc8b3b9a1e18e3b'"
?? /DEFAULTLIB:"MSVCRTD"
?? /DEFAULTLIB:"OLDNAMES"
可以看到,hello.obj須要鏈接libcpmt.lib、LIBCMT.lib和OLDNAMES.lib。當(dāng)我們使用“/MDd”參數(shù)編譯時,hello.obj就需要msvcprtd.lib、MSVCRTD.lib和OLDNAMES.lib,除此之外,編譯器還給鏈接器傳遞了“/manifestdependency”參數(shù),即manifest信息。
Q&A
Q:如果一個程序里面的不同obj文件或DLL文件使用了不同的CRT,會不會有問題?
A:這個問題實際上分很多種情況。如果程序沒有用到DLL,完全靜態(tài)鏈接,不同的obj在編譯時用到了不同版本的靜態(tài)CRT。由于目前靜態(tài)鏈接CRT只有多線程版,并且如果所有的目標(biāo)文件都統(tǒng)一使用調(diào)試版或發(fā)布版,那么這種情況下一般是不會有問題的。因為我們知道,目標(biāo)文件對靜態(tài)庫引用只是在目標(biāo)文件的符號表中保留一個記號,并不進行實際的鏈接,也沒有靜態(tài)庫的版本信息。
?????? 但是,如果程序涉及動態(tài)鏈接CRT,這就比較復(fù)雜了。因為不同的目標(biāo)文件如果依賴于不同版本的msvcrt.lib和msvcrt.dll,甚至有些目標(biāo)文件是依賴于靜態(tài)CRT,而有些目標(biāo)文件依賴于動態(tài)CRT,那么很有可能出現(xiàn)的問題就是無法通過鏈接。鏈接器對這種情況的具體反應(yīng)依賴于輸入目標(biāo)文件的順序,有些情況下它會報符號重復(fù)定義錯誤:
?????? MSVCRTD.lib(MSVCR80D.dll) : error LNK2005: _printf already defined in LIBCMTD.lib (printf.obj)
?????? 但是有些情況下,它會使鏈接順利通過,只是給出一個警告:
?????? LINK : warning LNK4098: defaultlib 'LIBCMTD' conflicts with use of other libs; use /NODEFAULTLIB:library
?????? 如果碰到上面這種靜態(tài)/動態(tài)CRT混合的情況,我們可以使用鏈接器的/NODEFAULTLIB來禁止某個或某些版本的CRT,這樣一般就能使鏈接順利進行。
?????? 最麻煩的情況應(yīng)該屬于一個程序所依賴的DLL分別使用不同的CRT,這會導(dǎo)致程序在運行時同時有多份CRT的副本。在一般情況下,這個程序應(yīng)該能正常運行,但是值得注意的是,你不能夠在這些DLL之間相互傳遞使用一些資源。比如兩個DLL A和B分別使用不同的CRT,那么應(yīng)該注意以下問題:
???? l???? 不能在A中申請內(nèi)存然后在B中釋放,因為它們分屬于不同的CRT,即擁有不同的堆,這包括C++里面所有對象的申請和釋放;
???? l???? 在A中打開的文件不能在B中使用,比如FILE*之類的,因為它們依賴于CRT的文件操作部分。
?????? 還有類似的問題,比如不能相互共享locale等。如果不違反上述規(guī)則,可能會使程序發(fā)生莫名其妙的錯誤并且很難發(fā)現(xiàn)。
?????? 防止出現(xiàn)上述問題的最好方法就是保證一個工程里面所有的目標(biāo)文件和DLL都使用同一個版本的CRT。當(dāng)然有時候事實并不能盡如人意,比如很多時候當(dāng)我們要用到第三方提供的.lib或DLL文件而對方又不提供源代碼時,就會比較難辦。
?????? Windows系統(tǒng)的system32目錄下有個叫msvcrt.dll的文件,它跟msvcr90.dll這樣的DLL有什么區(qū)別?
Q:為什么我用Visual C++ 2005/2008編譯的程序無法在別人的機器上運行?
A:因為Visual C++ 2005/2008編譯的程序使用了manifest機制,這些程序必須依賴于相對應(yīng)版本的運行庫。一個解決的方法就是使用靜態(tài)鏈接,這樣就不需要依賴于CRT的DLL。另外一個解決的方法就是將相應(yīng)版本的運行庫與程序一起發(fā)布給最終用戶。
創(chuàng)作挑戰(zhàn)賽新人創(chuàng)作獎勵來咯,堅持創(chuàng)作打卡瓜分現(xiàn)金大獎總結(jié)
- 上一篇: 程序员要如何学英语?
- 下一篇: 可视化的状态机(FSM)