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? |