error LNK2005:已經在libcpmtd.lib(xmutex.obj) 中定義

來源:互聯網
上載者:User

經常遇到這樣的問題, 這兒也算是一些總結了:


很久沒有寫程式設計入門知識的相關文章了,這篇文章要來談談程式庫 (Library) 連結,以及關於 MSVC 與 CRT 之間的種種恩怨情仇。

如果你使用的作業系統是 Linux、Mac 或其他非 Windows 平台,你可以忽略這篇文章;如果你使用的作業系統是 Windows 平台,但沒有用 Microsoft Visual Studio C++(以下簡稱為 MSVC)軟體撰寫 C++ 程式的話,這篇文章對你的協助可能很有限;但如果你的作業系統是 Windows,而且你使用的程式整合開發環境是 MSVC 軟體撰寫 C++ 程式的話,這篇文章應該能夠協助你釐清一些重要的基礎觀念。

身為程式設計者,在學習程式設計的過程中,你是否曾經遇過某些看起來不知所云的錯誤訊息,卻不知該如何解決。例如當你快快樂樂地寫完程式,並且確認所有的程式碼都能成功通過編譯之後,接著執行「建置方案」(Build Solution) 的步驟,結果卻跑出一堆莫名其妙的錯誤:

LIBCMTD.lib(mlock.obj) : error LNK2005: __lock 已在 MSVCRTD.lib(MSVCR80D.dll) 中定義過了
LIBCMTD.lib(mlock.obj) : error LNK2005: __unlock 已在 MSVCRTD.lib(MSVCR80D.dll) 中定義過了
LIBCMTD.lib(crt0.obj) : error LNK2005: _mainCRTStartup 已在 MSVCRTD.lib(crtexe.obj) 中定義過了

…………

LINK : warning LNK4098: 預設的程式庫 ‘MSVCRTD’ 與其他使用的程式庫衝突,請使用 /NODEFAULTLIB:library
LINK : warning LNK4098: 預設的程式庫 ‘LIBCMTD’ 與其他使用的程式庫衝突,請使用 /NODEFAULTLIB:library
D:\Workspace\CrtLibTest\Debug\CrtLibTest.exe : fatal error LNK1169: 找到有一或多個已定義的符號

以一般的情況來說,如果在你的程式專案中有使用某些由他人所撰寫的第三方程式庫或是開源專案的程式庫,比較容易會發生上述的錯誤狀況。從上述這些看似離奇而令人摸不著頭緒的錯誤訊息中,我們大概可以猜測問題點應該在於 LIBCMTD.lib 與 MSVCRTD.lib 這兩個程式庫身上。但到底什麼是 LIBCMTD.lib 和 MSVCRTD.lib。在我們的程式碼中有使用這些程式庫嗎。

 

答案是肯定的。

熟悉 C 語言的程式設計者都知道,如果要使用 printf()、scanf() 或者 fopen() 等等 C 語言的基本 I/O 操作函式時,首先必須用 #include 文法將 stdio.h 這個標題檔納入我們的程式碼中。藉由 stdio.h 中對這些 I/O 操作函式所做出的函式宣告 (function declaration),編譯器 (Compiler) 才得以確認 printf、scanf 以及 fopen 等等都是合法可用的函式。

而當我們撰寫的程式碼經過編譯器產出 OBJ 形式的檔案之後,需要再經由連結器 (Linker) 的處理常式,將程式碼中全部有使用到的函式定義 (function definition) 連結建置起來,才能夠產生出最後的程式執行檔。問題來了,我們知道 printf、scanf 以及 fopen 的函式宣告存在於 stdio.h 當中,但是這些傢夥的函式定義,也就是真正的實做程式碼,究竟存放在什麼地方呢。

在 C 語言的標準程式庫中。

由 C 語言所制訂的標準程式庫,稱之為「執行階段程式庫」,也就是 C Run-Time Library,通常可簡稱為 CRT。在 C 語言的標準程式庫中,包含了一組常用的基礎函式,例如 I/O 處理與字串操作程式等等,所以只要我們使用 C 語言撰寫程式碼,就一定要將編譯完成後的程式碼 OBJ 檔,連結至 C 語言的執行階段程式庫,才能夠產生出合法的 C 語言程式執行檔。

而 CRT 並非只有單一一種版本存在。事實上,除了可以依「除錯」與「釋出」用途分成兩個版本之外,兩者又可分別衍生分出「靜態連結」與「動態連結」兩種形式:

靜態連結: LIBCMTD.lib(除錯版本) LIBCMT.lib

動態連結: MSVCRTD.lib(除錯版本) MSVCRT.lib

雖然這四個 CRT 版本的用途與使用方式各不相同,但卻有個共通的特點,就是它們都是滿足執行緒安全需求,可在多執行緒程式碼中安全使用的程式庫版本。事實上,在過去 MSVC 6 的版本中,本來還有另外兩個 LIBCD.lib(除錯版本)與 LIBC.lib 程式庫,是專門給單執行緒程式使用的 CRT 版本,但是這兩個選項自 MSVC 2005 開始就從設定選項中被刪除掉了,所以現在大多數程式設計者使用的都是多執行緒的 CRT 版本。

在程式庫連結 (library linking) 的行為中,靜態連結和動態連結的分別,在於使用靜態連結時,會直接將程式庫的函式定義嵌入執行檔之中,而使用動態連結時,程式庫的函式定義則存在於另外的獨立檔案,通常是 DLL 格式的檔案中,然後與程式執行檔一同發佈給使用者。因此在檔案的尺寸上,使用動態連結的執行檔檔案,通常會比使用靜態連結的執行檔檔案來得更小一些。

使用動態連結 CRT 版本的好處,是能夠將經常使用到的標準程式庫們獨立出來,放在 Windows 的系統資料夾中,以減少我們建置出來的執行檔檔案尺寸。但反過來說,使用動態連結 CRT 版本的缺點也在於這些與執行檔相依為命的 DLL 檔案上。舉例來說,如果程式以 MSVC 2005 建置出 Debug 組態的執行檔,則此執行檔需要有 msvcr80d.dll 存在才能順利執行;如果是 Release 組態,則相依於 msvcr80.dll。但是如果你把相同的程式碼拿到 MSVC 2008 上建置,產生出來的執行檔則相依於 msvcr90d.dll 與 msvcr90.dll 兩個不同的 DLL 檔案。不同版本的 MSVC,都會有各自不同的相依 DLL 檔案。

在 MSVC 的程式專案中,如何指定程式碼要使用靜態連結或者動態連結的 CRT 版本。其實很容易,只要在專案屬性的「C/C++」頁面中,選擇「程式碼產生」(Code Generation) 子頁面,其中有個「執行階段程式庫」(Runtime Library) 的項目,也就是專案中用來設定 CRT 連結版本的地方。其中總共有四個選項,正好對應於上述靜態連結與動態連結的四個不同程式庫版本。 多執行緒偵錯 (/MTd):對應 LIBCMTD.lib 多執行緒 (/MT):對應 LIBCMT.lib 多執行緒偵錯 DLL (/MDd):對應 MSVCRTD.lib 多執行緒 DLL (/MD):對應 MSVCRT.lib

如果你沒有做任何設定就開始建置程式的話,MSVC 的預設選項則會使用動態連結的版本。



造成LNK2005錯誤主要有以下幾種情況:   
  1.重複定義全域變數。可能存在兩種情況:   
  A、對於一些初學編程的程式員,有時候會以為需要使用全域變數的地方就可以使用定義申明一下。其實這是錯誤的,全域變數是針對整個工程的。正確的應該是在一個CPP檔案中定義如下:int   g_Test;那麼在使用的CPP檔案中就應該使用:extern   int   g_Test即可,如果還是使用int   g_Test,那麼就會產生LNK2005錯誤,一般錯誤錯誤資訊類似:AAA.obj   error   LNK2005   int   book   c。   already   defined   in   BBB.obj。切記的就是不能給變數賦值否則還是會有LNK2005錯誤。   
                這裡需要的是“聲明”,不是“定義”。根據C++標準的規定,一個變數是聲明,必須同時滿足兩個條件,否則就是定義:   
  (1)聲明必須使用extern關鍵字;(2)不能給變數賦初值   
  所以,下面的是聲明:   
  extern   int   a;   
  下面的是定義   
  int   a;   int   a   =   0;   extern   int   a   =0;   
  B、對於那麼編程不是那麼嚴謹的程式員,總是在需要使用變數的檔案中隨意定義一個全域變數,並且對於變數名也不予考慮,這也往往容易造成變數名重複,而造成LNK2005錯誤。   
    
  2.標頭檔的包含重複。往往需要包含的標頭檔中含有變數、函數、類的定義,在其它使用的地方又不得不多次包含之,如果標頭檔中沒有相關的宏等防止重複的連結的措施,那麼就會產生LNK2005錯誤。解決辦法是在需要包含的標頭檔中做類似的處理:#ifndef   MY_H_FILE       //如果沒有定義這個宏   
  #define   MY_H_FILE       //定義這個宏   
  …….       //標頭檔主體內容   
  …….   
  #endif   
  上面是使用宏來做的,也可以使用先行編譯來做,在標頭檔中加入:   
  #pragma   once   

  //標頭檔主體   

 

  3.使用第三方的庫造成的。這種情況主要是C運行期函數庫和MFC的庫衝突造成的。具體的辦法就是將那個提示出錯的庫放到另外一個庫的前面。另外選擇不同的C函數庫,可能會引起這個錯誤。微軟和C有兩種C運行期函數庫,一種是普通的函數庫:LIBC.LIB,不支援多線程。另外一種是支援多線程的:msvcrt.lib。如果一個工程裡,這兩種函數庫混合使用,可能會引起這個錯誤,一般情況下它需要MFC的庫先於C運行期函數庫被連結,因此建議使用支援多線程的msvcrt.lib。所以在使用第三方的庫之前首先要知道它連結的是什麼庫,否則就可能造成LNK2005錯誤。如果不得不使用第三方的庫,可以嘗試按下面所說的方法修改,但不能保證一定能解決問題,前兩種方法是微軟提供的:   
  A、選擇VC菜單Project->Settings->Link->Catagory選擇Input,再在Ignore   libraries   的Edit欄中填入你需要忽略的庫,如:Nafxcwd.lib;Libcmtd.lib。然後在Object/library   Modules的Edit欄中填入正確的庫的順序,這裡需要你能確定什麼是正確的順序,呵呵,God   bless   you。   
  B、選擇VC菜單Project->Settings->Link頁,然後在Project   Options的Edit欄中輸入/verbose:lib,這樣就可以在編譯連結程式過程中在輸出視窗看到連結的順序了。   
  C、選擇VC菜單Project->Settings->C/C++頁,Catagory選擇Code   Generation後再在User   Runtime   libraray中選擇MultiThread   DLL等其他庫,逐一嘗試。   
  關於編譯器的相關處理過程,參考:   

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.