[C-C++]你所不知道的C和C++執行庫
原創文章,歡迎轉載。轉載請註明:轉載自 祥的部落格
文章目錄
原文:你所不知道的C和C++執行庫
轉載後只做了格式上的編輯,原文如下:
週五晚,小雨,少見的未加班。無聊,遂準備寫一篇部落格,介紹一下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.0
使用的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.0
使用的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 於浙江杭州