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