當前位置: 首頁>>技術問答>>正文


C++ undefined reference/unresolved external symbol問題原因分析

C++程序編譯時,經常遇到undefined reference to或者unresolved external symbol問題,這裏闡述一下問題原因及解決方法。

C++編譯的步驟

要解決這個問題,先要理解C++的編譯步驟,看看問題發生在哪個階段。

1. 字符映射。物理源文件的字符被替換為用於內部表示的基礎字符集(這個基礎字符集取決於編譯器實現,主要解決特殊字符的問題)。

2. 代碼行合並。把”\”分割的多行拚接為一行。

3. Token化。Token可以理解為一個字符串。將源碼解析為Token+空格序列。

4. 預處理。執行預處理指令,展開宏調用,執行Pragma二元運算符。

5. 源字符集轉可執行字符集。源字符級是第1步的轉化結果,可執行字符集處理一般是將某些可見字符轉為字節表達。

6. 連接Token。將有語義關係的token連接到一起。

7. 語法語義分析。

8. 處理模板。

9. 連接。解析所有外部引用。之前處理都是單文件的,這裏開始處理一個文件對另外一個文件(或者Lib庫)變量和函數的引用,將所有源合並為可執行的程序映像。

問題分析

變量或者函數未定義的問題(undefined reference to或者unresolved external symbol問題`)就發生在第9步連接階段。通常這個報錯意味需要把相關的代碼和包依賴加進來一起編譯。

假設先在a.cpp中定義了符號a(函數或者變量),然後在b.cpp中聲明並使用a。在連接之前的處理步驟中,會假設a是合法的(在其他文件中有定義)。到了連接階段,開始校驗符號a的合法性,這時候找到a所在的定義文件並和b.cpp的編譯處理結果做連接。

如果使用微軟的VisualStudio(MSVS), 會生成多個.lib文件,這類文件存儲了引入符號表和導出符號表。連接階段會使用這個兩個表輔助符號校驗。其他編譯器也有類似機製。

連接是的未定義問題,對應的錯誤消息一般為:MSVS中是error LNK2001, error LNK1120, error LNK2019;GCC是undefined reference to xxx

下麵是一段有問題的示例代碼:

struct X
{
   virtual void foo();
};
struct Y : X
{
   void foo() {}
};
struct A
{
   virtual ~A() = 0;
};
struct B: A
{
   virtual ~B(){}
};
extern int x;
void foo();
int main()
{
   x = 0;
   foo();
   Y y;
   B b;
}

如果用gcc編譯這段代碼的話,會報下麵的錯誤:

/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status

如果用MSVS編譯的話,會報下麵的錯誤:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals

常見的具體原因如下:

  • 某些庫文件在鏈接的時候沒有引入,比如:使用數學函數沒有-lm,使用多線程沒有-lpthread等
  • 聲明了某個變量或者函數,但是沒有定義(或者說實現)
  • 模板的實現不可見
  • C定義的符號在C++中使用(比如:C/C++內部對函數名的表示不一樣)
  • 多個源文件同名(處於不同文件夾)
  • 循環依賴

更多解決方案,可以google下這個問題:What is an undefined reference/unresolved external symbol error and how do I fix it?

本文由《純淨天空》出品。文章地址: https://vimsky.com/zh-tw/article/2590.html,未經允許,請勿轉載。