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?