1. 程式人生 > >C++編譯連接過程中關於符號表的報錯分析

C++編譯連接過程中關於符號表的報錯分析

區別 生成 fin 結構 undefine 編譯預處理 析構函數 編譯 還需

是這樣的,在學習鄭莉老師的多文件結構和編譯預處理命令章節時候,看到書裏有這麽一張圖描述如下:#include指令作用是將指定的文件嵌入到當前源文件中#include指令所在的位置。
技術分享圖片

然後我就想5_10.cpp主程序直接include了point.cpp也可以吧(因為point.cpp裏include了point.h,這樣既有聲明又有定義)。沒錯,書中繼續描述被嵌入的文件可以是.h文件,也同樣可以是.cpp文件。但是當我在eclipse for c++環境裏驗證的時候卻打臉了,讓我一度懷疑是不是不能#include .cpp。

eclipse中在連接的那一步報錯了,如下:
技術分享圖片

並沒有認真看eclipse中報錯內容的我在vim中一頓操作驗證結果明明可以引入cpp的啊。上半部分是include .h的測試,下半部分是include .cpp的測試,都可以看出在生成.o文件也就是編譯生成目標代碼並沒啥錯,就在連接步驟引入.h的卻報錯,引入.cpp卻正常(這結果與在eclipse中剛好相反啊),這不禁讓我思考了起來,哦發現了,在命令行中我是用命令指定編譯哪個cpp文件,在引入.h的test.cpp測試中我只編譯了test而沒有編譯也沒連接point.cpp,所以連接時候找不到函數地址就很正常了(可以註意到它報的錯是undefined,而eclipse中報的是duplicate,這就是區別...)
技術分享圖片

回到eclipse,往上翻錯誤,看到eclipse好像是把我項目底下所有cpp都給編譯了,一看果然是...emmm make all。其實編譯就編譯吧也沒有啥影響最後別連接那些我沒用的就行,但是可以看到這個真的是linker 了all啊..

技術分享圖片
由於我在main.cpp中include了6文件夾的Point.cpp,這就相當於把6文件夾下的Point.cpp編譯了兩次(產生了兩個關於point.cpp的符號表,關於什麽是符號表,就是把程序中各個標識符名稱和它們在各段中的地址關聯起來的數據結構,見下圖)。然後在連接的時候,是將各個編譯單元的目標文件和運行庫當中被調用過的單元加以合並,經過合並後不同編譯單元代碼段和數據段就分別合並到一起,與此同時,各個目標文件的符號表也可以被綜合起來,連接最後符號表的每個條目都必須有確定的地址。然而eclipse連接報錯就報錯在符號表的函數地址應該是什麽,main中所引用的Point.cpp和6文件夾的Point.cpp是一個東西,但卻在生成.o文件時候符號表中的Point類的各個函數各自有了地址。

技術分享圖片

符號表能夠被正確綜合的一個前提是,對於同一個符號,只在剛好一個編譯單元中有定義,而在其他編譯單元中是未定義的。之所以有這個要求是因為合並後符號表中各個符號的地址需要根據該符號在有定義的編譯單元中的相對地址來確定。若在多個編譯單元中同一個符號都有定義地址,那麽它的地址將無所適從,就會出現符號定義沖突的連接錯誤。所以可以看到eclipse給出的錯誤提示是"duplicate symbol".

技術分享圖片

其中形如__ZN5...的名字是函數名,在符號表中函數並不只以它在源程序中的名字命名,函數在符號表中的名字至少包括源程序的函數名和參數表類型信息。因為函數可以重載,由於符號表中沒有專門的類型信息,參數表信息只能在名字中有所體現,否則在目標文件中無法對函數名相同單參數不同的函數加以區分。look,其中move函數就是我point類中定義的一個成員函數,剩下的都是構造函數(我定義的+類中默認的其他構造)或析構函數把。所以,看來在eclipse中要非想引用cpp文件就要自己重寫eclipse中的make文件咯,另外還需要認真看報錯咯,不能一頓盲猜測。

C++編譯連接過程中關於符號表的報錯分析