glibc與MSVC CRT,crt編譯錯誤及解決

2021-04-27 22:09:52 字數 2860 閱讀 2760

http://book.51cto.com  2009-04-22 15:29  俞甲子/石凡/潘愛民  電子工業出版社  

11.2.3  glibc與msvc crt(4)

當你在程式裡包含了某個c++標準庫的標頭檔案時,msvc編譯器就認為該源**檔案是乙個c++源**程式,它會在編譯時根據編譯選項,在目標檔案 的".drectve"段(還記得第2章中的directive吧?)相應的c++標準庫鏈結資訊。比如我們用c++寫乙個"hello world"程式:

// hello.cpp

#include

int main()

然後將它編譯成目標檔案,並檢視它的".drectve"段的資訊:

dump of file msvcprt.obj

file type: coff object
linker directives

-----------------

/defaultlib:"libcpmt"

/defaultlib:"libcmt"

/defaultlib:"oldnames"

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,那麼應該注意以下問題:

不能在a中申請記憶體然後在b中釋放,因為它們分屬於不同的crt,即擁有不同的堆,這包括c++裡面所有物件的申請和釋放;

在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。另外乙個解決的方法就是將相應版本的執行庫與程式一起發布給終端使用者。

glib與glibc的區別

glib是gtk 庫和gnome的基礎。glib可以在多個平台下使用,比如linux unix windows等。glib為許多標準的 常用的c語言結構提供了相應的替代物。glib中定義了好多有用的資料結構,如單 雙 向連表,可變長的陣列等,執行緒池等有用的東西。使用glib庫的程式都應該包含gli...

glibc,執行緒安全與可重入

1.glibc是什麼,以及與gcc的關係?glibc是gnu發布的libc庫,也即c執行庫。glibc是linux 系統中最底層的api 應用程式開發介面 幾乎其它任何的執行庫都會倚賴於glibc。glibc除了封裝linux作業系統所提供的系統服務外,它本身也提供了許多其它一些必要功能服務的實現,...

Glibc 安裝指南

glibc 安裝指南 適用於2.3 2.4 2.5 2.6 2.7 編譯前的預備知識與要點提示 glibc 2.3.6 建議使用 gcc 4.0 編譯,glibc 2.4 2.5 建議使用 gcc 4.1 編譯,glibc 2.6 2.7 建議使用 gcc 4.2 編譯。所有這些版本最低要求使用 g...