C++工程編譯之“error LNK2001: 無法解析的外部符號”
今天一整天都在折騰“error LNK2001: 無法解析的外部符號”,就在頭疼不已的時候,總算是找到問題原因了:各個動態連結庫的編譯方式必須統一才行,要不然很容易對庫函式的引用產生衝突。簡單來說就是,如果使用的第三方函式庫編譯方式採用/MD,那麼主工程也應該使用/MD。我使用了libevent,而主工程預設採用/MT,所以需要忽略一大堆的函式庫,我還納悶呢,怎麼會這麼奇怪!!今天總算是解決了長久以來的困惑了。
下面引用一篇文章的描述:[Z]VC執行庫版本不同導致連結.LIB靜態庫時發生重複定義問題的一個案例分析和總結
再參考一篇:msvcprt.lib(MSVCP90.dll) : error LNK2005:已經在libcpmtd.lib(xmutex.obj) 中定義
Background
MSDN中對於在不同的配置下Link的LIB作了說明:
C Runtime Library:
C++ Standard Library:
編譯器會自動根據編譯選項,選擇對應的LIB檔案。一般情況下這不會出現問題。
然而,在部分情況下,一旦你的程式的各個部分(LIB, OBJ…)並非由相同的編譯選項編譯出,而Link在一起的話,會出現各種各樣的看似很難解決的問題,這類問題主要以重複定義的錯誤形式存在,通常的解決方法也很簡單,就是選擇同樣的編譯選項進行編譯之後再Link。
關於連結方式補充
1、為什麼選擇/MD,不選/MT?
(1)程式就不需要靜態連結執行時庫,可以減小軟體的大小;
(2)所有的模組都採用/MD,使用的是同一個堆,不存在A堆申請,B堆釋放的問題;
(3)使用者機器可能缺少我們編譯時使用的動態執行時庫。(補充:如果我們軟體有多個DLL,採用/MT體積增加太多,則可以考慮/MD + 自帶系統執行時庫)
選擇/MD要注意“多個模組選擇不同版本執行庫”的問題:
-
多個dll被一個exe LoadLibrary載入,如果這些dll使用的執行時庫是不同的,那麼可能出現載入失敗,原因可能是舊版本的執行時庫已經在了,而某個dll它需要的是新版本的執行時庫,舊版本不符合要求。
-
如果工程裡所有的模組都是自己寫的或者可以完全控制的,那麼這個問題不難解決,只需要在工程設定裡都設定/MD,然後在相同的環境下編譯一次就行。但是假如這個模組是外界提供的呢?
-
可能存在這種情況:A動態庫使用了B靜態庫,B靜態庫使用了C動態庫,B靜態庫是外界提供的,我們要使用它,但無法修改它,我們也無法接觸到C動態庫。如果C動態庫使用的執行時庫版本跟編譯A動態庫的本地使用的不一致,那麼A動態庫裡的嵌入資訊就會記錄兩個不同版本的執行時庫,它被載入的時候,可能會選擇版本新的。假設A動態庫被一個exe LoadLibrary載入,而這個exe本身的執行時庫是舊的,這樣就會導致A動態庫載入失敗,即便把新的執行時庫拷貝到目錄下也不行,因為exe這個程序已經載入了那個舊的執行時庫。這時候必須使用manifest檔案指定嵌入到A動態庫裡的執行時庫為某個版本,忽略掉C動態庫使用的執行時庫版本。
-
這個問題挺複雜的,我心思沒去驗證windows的PE檔案載入會對執行時庫做什麼樣的優先選擇、執行時庫在靜態庫裡的記錄…。只要記住,給外界使用的元件版本儘量避免使用/MD(這樣會導致膨脹嗎?據說,安裝包可以做位元組流式壓縮)。
-
附上另一個問題:靜態庫的依賴關係:exe–>libA–>libB,現在不想讓exe接觸到libB,於是把libA的librarian選項–>General選項–>Link Library Dependencies設定為Yes,這樣即可,libA會包含libB,exe只需要接觸libA。另外需要特別注意,libA對libB的依賴只需要且只能在Solution的Project Dependencies裡設定,如果在libA的程式碼裡寫了”#pragma comment(lib, “libB.lib”)”,會導致exe在link libA的時候提示找不到libA。如果exe還出現link錯誤,那一定是VS抽筋了:)
2、為什麼選擇/MT,不選擇/MD?
(1)有些系統可能沒有程式所需要版本的執行時庫,程式必須把執行時庫靜態連結上。
(2)減少模組對外界的依賴。
選擇/MT 時存在一個“堆空間釋放”的問題:
-
不同的模組各自有一份C執行時庫程式碼、或者根本沒有C執行時庫,導致了各個模組會有各自的堆。如果在A堆中申請空間,到B堆中釋放就會有崩潰,在模組A申請的空間,必須在模組A中釋放。
3、多個模組,必須選擇相同的執行時庫。
Case Study
之前剛下載了ANTLR,在準備編譯它的Example的時候發現了下面的Build錯誤(我自己為這個例子建立了VS的專案,當前配置為動態連結Runtime庫,Debug版):
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: __thiscall std::basic_string<char,struct std::char_traits,class std::allocator >::~basic_string<char,struct std::char_traits,class std::allocator >(void)” (??1?[email protected]@[email protected]@V?KaTeX parse error: Expected 'EOF', got '&' at position 218: …ocator<char> > &̲ __thiscall std…[email protected]?[email protected]@[email protected]@[email protected]@[email protected]@Z) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: void __thiscall std::basic_string<char,struct std::char_traits,class std::allocator >::reserve(unsigned int)” ([email protected]?[email protected]@[email protected]@V?KaTeX parse error: Expected group after '_' at position 144: …: unsigned int _̲_thiscall std::…[email protected]?[email protected]@[email protected]@[email protected]@QBEIXZ) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: class std::basic_string<char,struct std::char_traits,class std::allocator > & __thiscall std::basic_string<char,struct std::char_traits,class std::allocator >::operator+=(class std::basic_string<char,struct std::char_traits,class std::allocator > const &)” (??Y?[email protected]@[email protected]@V?KaTeX parse error: Expected group after '_' at position 141: …2005: "public: _̲_thiscall std::…[email protected]?[email protected]@[email protected]@[email protected]@[email protected]@@Z) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: "public: char const & __thiscall std::basic_string<char,struct std::char_traits,class std::allocator >::operator[](unsigned int)const " (??A?[email protected]@[email protected]@V?KaTeX parse error: Expected 'EOF', got '&' at position 221: …ocator<char> > &̲ __thiscall std…[email protected]?[email protected]@[email protected]@[email protected]@[email protected]@@Z) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: class std::basic_string<char,struct std::char_traits,class std::allocator > & __thiscall std::basic_string<char,struct std::char_traits,class std::allocator >::operator=(char const *)” (??4?[email protected]@[email protected]@V?KaTeX parse error: Expected group after '_' at position 138: …2005: "public: _̲_thiscall std::…[email protected]?[email protected]@[email protected]@[email protected]@[email protected]@Z) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “class std::basic_string<char,struct std::char_traits,class std::allocator > __cdecl std::operator+<char,struct std::char_traits,class std::allocator >(class std::basic_string<char,struct std::char_traits,class std::allocator > const &,class std::basic_string<char,struct std::char_traits,class std::allocator > const &)” (??[email protected]@[email protected]@V?[email protected]?[email protected]@[email protected]@[email protected]@[email protected]) already defined in antlr.lib(MismatchedCharException.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: "public: char const * __thiscall std::basic_string<char,struct std::char_traits,class std::allocator >::c_str(void)const " ([email protected]?[email protected]@[email protected]@V?KaTeX parse error: Expected group after '_' at position 129: …LNK2005: "bool _̲_cdecl std::ope…?MDU?[email protected]@[email protected]@[email protected]@YA_NABV?[email protected]@[email protected]@V?KaTeX parse error: Expected 'EOF', got '&' at position 186: …traits<char> > &̲ __thiscall std…[email protected]?KaTeX parse error: Expected 'EOF', got '&' at position 219: …traits<char> > &̲ __thiscall std…[email protected]?KaTeX parse error: Expected 'EOF', got '&' at position 195: …traits<char> > &̲ __cdecl std::e…[email protected]?KaTeX parse error: Expected group after '_' at position 139: … "public: void _̲_thiscall std::…[email protected]?KaTeX parse error: Expected group after '_' at position 142: …: "public: int _̲_thiscall std::…[email protected]?KaTeX parse error: Expected group after '_' at position 151: …c: static bool _̲_cdecl std::cha…[email protected]@[email protected]@[email protected]) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: static int __cdecl std::char_traits::eof(void)” ([email protected]?KaTeX parse error: Expected group after '_' at position 132: …: "public: int _̲_thiscall std::…[email protected]?KaTeX parse error: Expected group after '_' at position 202: …aits<char> > * _̲_thiscall std::…[email protected]?[email protected]?KaTeX parse error: Expected group after '_' at position 133: … "public: char _̲_thiscall std::…[email protected]?KaTeX parse error: Expected group after '_' at position 139: …: "public: int _̲_thiscall std::…[email protected]@[email protected]@[email protected]) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: class std::basic_ostream<char,struct std::char_traits > & __thiscall std::basic_ostream<char,struct std::char_traits >::flush(void)” ([email protected]?[email protected]@[email protected]@@[email protected]@[email protected]) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: "public: class std::basic_ostream<char,struct std::char_traits > * __thiscall std::basic_ios<char,struct std::char_traits >::tie(void)const " ([email protected]?[email protected]@[email protected]@@[email protected]@QBEPAV?[email protected]@[email protected]@@[email protected]) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: "public: bool __thiscall std::ios_base::good(void)const " ([email protected][email protected]@@QBE_NXZ) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: void __thiscall std::basic_ostream<char,struct std::char_traits >::_Osfx(void)” ([email protected]?[email protected]@[email protected]@@[email protected]@QAEXXZ) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: void __thiscall std::basic_streambuf<char,struct std::char_traits >::_Lock(void)” ([email protected]?[email protected]@[email protected]@@[email protected]@QAEXXZ) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “public: void __thiscall std::basic_streambuf<char,struct std::char_traits >::_Unlock(void)” ([email protected]?[email protected]@[email protected]@@[email protected]@QAEXXZ) already defined in antlr.lib(CharScanner.obj)
1>msvcprtd.lib(MSVCP80D.dll) : error LNK2005: “class std::basic_ostream<char,struct std::char_traits > & __cdecl std::operator<<<char,struct std::char_traits,class std::allocator >(class std::basic_ostream<char,struct std::char_traits > &,class std::basic_string<char,struct std::char_traits,class std::allocator > const &)” (??[email protected]@[email protected]@V?