error link總結_第1頁
error link總結_第2頁
error link總結_第3頁
error link總結_第4頁
error link總結_第5頁
已閱讀5頁,還剩10頁未讀, 繼續(xù)免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、already defined in *.obj“符號已定義”問題原理及解決方案C+Base 2010-06-15 15:08:25 閱讀103 評論0   字號:大中小 訂閱 造成LNK2005錯誤主要有以下幾種情況: 1重復定義全局變量??赡艽嬖趦煞N情況: A、對于一些初學編程的程序員,有時候會以為需要使用全局變量的地方就可以使用定義申明一下。其實這是錯誤的,全局變量是針對整個工程的。正確的應該是在一個CPP文件中定義如下:int g_Test;那么在使用的CPP文件中就應該使用:extern int g_Test即可,如果還是使用int g_Test,那么就

2、會產生LNK2005錯誤,一般錯誤錯誤信息類似:AAA.obj error LNK2005 int book c?book3HA already defined in BBB.obj。切記的就是不能給變量賦值否則還是會有LNK2005錯誤。              這里需要的是“聲明”,不是“定義”!根據C+標準的規(guī)定,一個變量是聲明,必須同時滿足兩個條件,否則就是定義: (1)聲明必須使用extern關鍵字;(2)不能給變量賦初值 所以,下面的是聲明: extern in

3、t a; 下面的是定義 int a; int a = 0; extern int a =0; B、對于那么編程不是那么嚴謹的程序員,總是在需要使用變量的文件中隨意定義一個全局變量,并且對于變量名也不予考慮,這也往往容易造成變量名重復,而造成LNK2005錯誤。 2頭文件的包含重復。往往需要包含的頭文件中含有變量、函數、類的定義,在其它使用的地方又不得不多次包含之,如果頭文件中沒有相關的宏等防止重復鏈接的措施,那么就會產生LNK2005錯誤。解決辦法是在需要包含的頭文件中做類似的處理:#ifndef MY_H_FILE      /如果沒有定義這

4、個宏 #define MY_H_FILE      /定義這個宏 .      /頭文件主體內容 . #endif 上面是使用宏來做的,也可以使用預編譯來做,在頭文件中加入: #pragma once /頭文件主體 3使用第三方的庫造成的。這種情況主要是C運行期函數庫和MFC的庫沖突造成的。具體的辦法就是將那個提示出錯的庫放到另外一個庫的前面。另外選擇不同的C函數庫,可能會引起這個錯誤。微軟和C有兩種C運行期函數庫,一種是普通的函數庫:LIBC.LIB,不支持多線程。另外一種是支持多線程的:ms

5、vcrt.lib。如果一個工程里,這兩種函數庫混合使用,可能會引起這個錯誤,一般情況下它需要MFC的庫先于C運行期函數庫被鏈接,因此建議使用支持多線程的msvcrt.lib。所以在使用第三方的庫之前首先要知道它鏈接的是什么庫,否則就可能造成LNK2005錯誤。如果不得不使用第三方的庫,可以嘗試按下面所說的方法修改,但不能保證一定能解決問題,前兩種方法是微軟提供的: A、選擇VC菜單Project->Settings->Link->Catagory選擇Input,再在Ignore libraries 的Edit欄中填入你需要忽略的庫,如:Nafxcwd.lib;Libcmtd.

6、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等其他庫

7、,逐一嘗試。 關于編譯器的相關處理過程,參考: 這就是我所遇到過的LNK2005錯誤的幾種情況,肯定還有其他的情況也可能造成這種錯誤,所以我不希望你在看完這篇文章以后,再遇到LNK2005錯誤時候,不動腦筋的想對號入座的排除錯誤。編程的過程就是一個思考的過程,所以還是多多開動你的頭腦,那樣收獲會更多! 附錄:編譯器處理相關 一.預處理器-編譯器-匯編器-鏈接器預處理器會處理相關的預處理指令,一般是以"#"開頭的指令。如:#include "xx.h" #define等。編譯器把對應的*.cpp翻譯成*.s文件(匯編語言)。匯編器則處理*.s生成對應的*.

8、o文件(obj目標文件)最后鏈接器把所有的*.o文件鏈接成一個可執(zhí)行文件(?.exe)1.部件:首先要知道部件(可以暫且狹義地理解為一個類)一般分為頭文件(我喜歡稱為接口,如:*.h)及實現(xiàn)文件(如:*.cpp)。一般頭文件會是放一些用來作聲明的東東作為接口而存在的。而實現(xiàn)文件主要是實現(xiàn)的具體代碼。2.編譯單個文件:記住IDE在bulid文件時只編譯實現(xiàn)文件(如*.cpp)來產生obj,在vc下你可以對某個?.cpp按下ctrl+f7單獨編譯它生成對應一個?.obj文件。在編譯?.cpp時IDE會在?.cpp中按順序處理用#include包括進來的頭文件(如果該頭文件中又#include有文件

9、,同樣會按順序跟進處理各個頭文件,如此遞歸。)3.內部鏈接與外部鏈接:內、外鏈接是比較基礎的東東,但是也是新手最容易錯的地方,所以這里有必要祥細討論一下。內部鏈接產生的符號只在本地?.obj中可見,而外部鏈接的符號是所有*.obj之間可見的。如:用inline的是內部鏈接,在文件頭中直接聲明的變量、不帶inline的全局函數都是外部鏈接。在文件頭中類的內部聲明的函數(不帶函數體)是外部鏈接,而帶函數體一般會是內部鏈接(因為IDE會盡量把它作為內聯(lián)函數)認識內部鏈接與外部鏈接有什么作用呢?下面用vc6舉個例子:/ 文件main.cpp內容:void main()/ 文件t1.cpp內容:#inc

10、lude "a.h"void Test1() Foo(); / 文件t2.cpp內容:#include "a.h"void Test2() Foo(); / 文件a.h內容:void Foo( ) 好,用vc生成一個空的console程序(File - new - projects - win32 console application),并關掉預編譯選項開關(project - setting - Cagegoryrecompiled Headers - Not using precompiled headers)現(xiàn)在你打開t1.cpp按ctrl+f7

11、編譯生成t1.obj通過打開t2.cpp按ctrl+f7編譯生成t2.obj通過而當你鏈接時會發(fā)現(xiàn):Linking.t2.obj : error LNK2005: "void _cdecl Foo(void)" (?FooYAXXZ) already defined in t1.obj這是因為:1. 編譯t1.cpp在處理到#include "a.h"中的Foo時看到的Foo函數原型定義是外部鏈接的,所以在t1.obj中記錄Foo符號是外部的。2. 編譯t2.cpp在處理到#include "a.h"中的Foo時看到的Foo函數原型定

12、義是外部鏈接的,所以在t2.obj中記錄Foo符號是外部的。3. 最后在鏈接 t1.obj 及 t2.obj 時, vc發(fā)現(xiàn)有兩處地方(t1.obj和t2.obj中)定義了相同的外部符號(注意:是定義,外部符號可以多處聲明但不可多處定義,因為外部符號是全局可見的,假設這時有t3.cpp聲明用到了這個符號就不知道應該調用t1.obj中的還是t2.obj中的了),所以會報錯。解決的辦法有幾種: a.將a.h中的定義改寫為聲明,而用另一個文件a.cpp來存放函數體。(提示:把上述程序改來試試)(函數體放在其它任何一個cpp中如t1.cpp也可以,不過良好的習慣是用對應cpp文件來存放)。這時包括a.

13、h的文件除了a.obj中有函數體代碼外,其它包括a.h的cpp生成的obj文件都只有對應的符號而沒有函數體,如t1.obj、t2.obj就只有符號,當最后鏈接時IDE會把a.obj的Foo()函數體鏈接進exe文件中,并把t1.obj、t2.obj中的Foo符號轉換成對應在函數體exe文件中的地址。另外:當變量放在a.h中會變成全局變量的定義,如何讓它變?yōu)槁暶髂?例如: 我們在a.h中加入:class CFoo;CFoo* obj;這時按f7進行build時出現(xiàn):Linking.t2.obj : error LNK2005: "class CFoo * obj" (?obj

14、3PAVCFooA) already defined in t1.obj一個好辦法就是在a.cpp中定義此變量( CFoo* obj,然后拷貝此定義到a.h文件中并在前面加上extern(extern CFoo* obj如此就可通過了。當然extern也可以在任何調用此變量的位置之前聲明,不過強烈建議不要這么作,因為到處作用extern,會導致接口不統(tǒng)一。良好的習慣是接口一般就放到對應的頭文件。b. 將a.h中的定義修改成內部鏈接,即加上inline關鍵字,這時每個t1.obj和t2.obj都存放有一份Foo函數體,但它們不是外部符號,所以不會被別的obj文件引用到,故不存在沖突。(提示:把上

15、述程序改來試試)另外我作了個實驗來驗證”vc是把是否是外部符號的標志記錄在obj文件中的“(有點繞口)??梢钥纯?,如下:(1)文件內容:/ 文件main.cpp內容:void main()/ 文件t1.cpp內容:#include "a.h"void Test1() Foo(); / 文件t2.cpp內容:#include "a.h"void Test2() Foo(); / 文件a.h內容:inline void Foo( ) (2) 選t1.cpp按ctrl+f7單獨編譯,并把編譯后的t1.obj修改成t1.obj_inline(3) 選t2.cpp

16、按ctrl+f7單獨編譯,并把編譯后的t2.obj修改成t2.obj_inline(4) 把除了t1.obj_inline及t2.obj_inline外的其它編譯生成的文件刪除。(5) 修改a.h內容為:void Foo( ) ,使之變?yōu)榉莾嚷?lián)函數作測試(6) rebuild all所有文件。這時提示:Linking.t2.obj : error LNK2005: "void _cdecl Foo(void)" (?FooYAXXZ) already defined in t1.objDebug/cle.exe : fatal error LNK1169: one or m

17、ore multiply defined symbols found(7) 好,看看工程目錄下的debug目錄中會看到新生成的obj文件。下面我們來手工鏈接看看,打開菜單中的project - setting - Link,拷貝Project options下的所有內容,如下:kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.

18、lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:yes /pdb:"Debug/cle.pdb" /debug /machine:I386 /out:"Debug/cle.exe" /pdbtype:sept 把它修改成:Link.exe kernel32.lib user32.lib

19、 gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:console /incremental:y

20、es /pdb:"Debug/cle.pdb" /debug /machine:I386 /out:"Debug/cle.exe" /pdbtype:sept Debug/t1.obj Debug/t2.obj Debug/main.objpause注意前面多了Link.exe,后面多了Debug/t1.obj Debug/t2.obj Debug/main.obj以及最后一個pause批處理命令,然后把它另存到工程目錄(此目錄下會看到debug目錄)下起名為link.bat運行它,就會看到:t2.obj : error LNK2005: "v

21、oid _cdecl Foo(void)" (?FooYAXXZ) already defined in t1.objDebug/cle.exe : fatal error LNK1169: one or more multiply defined symbols found很好,我們鏈接原來的obj文件得到的效果跟在vc中用rebuild all出來的效果一樣。那么現(xiàn)在如果我們把備份出來的t1.obj_inline覆蓋t1.obj而t2.obj_inline覆蓋t2.obj再手動鏈接應該會是不會出錯的,因為原t1.obj_inline及t2.obj_inline中存放的是內部鏈接符

22、號。好運行Link.bat,果然不出所料,鏈接成功了,看看debug目錄下多出了一個exe文件。這就說明了內或外符號在obj有標志標識!(提示:上述為什么不用vc的f7build鏈接呢,因為文件時間改變了,build會重新生成新的obj,所以我們用手動鏈接保證obj不變)注bj信息可用dumpbin.exe查看4.#include規(guī)則:有很多人不知道#include 文件該放在何處?1). 增強部件自身的完整性:為了保證部件完整,部件的cpp實現(xiàn)文件(如test.cpp)中第一個#include的應當是它自身對應的頭文件(如test.h)。(除非你用預編譯頭文件, 預編譯頭必須放在第一個)。這

23、樣就保證了該部件頭文件(test.h)所必須依賴的其它接口(如a.h等)要放到它對應的文件頭中(test.h),而不是在cpp中(test.cpp)把所依賴的其它頭文件(a.h等)移到其自身對應的頭文件(test.h等)之前(因為這樣強迫其它包括此部件的頭文件(test.h)的文件(b.cpp)也必須再寫一遍include(即b.cpp若要#include "test.h"也必須#include "a.h")”。另外我們一般會盡量減少文件頭之間的依賴關系,看下面:2). 減少部件之間的依賴性:在1的基礎上盡量把#include到的文件放在cpp中包括。這就要求我們一般不要在頭文件中直接引用其它變量的實現(xiàn),而是把此引用搬到實現(xiàn)文件中。例如: / 文件foo.h:class CFoovoid Foo();/ 文件test.h:#include "foo.h"class CTestCFoo* m_pFoo;public:CTest() : m_pFoo(NULL)void Test() if(m_pFoo) m_pFoo->Foo();.;/ 文件test.cpp:#include "test.h".如上文件test.h中我們其實可以#include

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

最新文檔

評論

0/150

提交評論