当前位置: 首页>>技术问答>>正文


C++ undefined reference/unresolved external symbol问题原因分析

qingchuan 技术问答, 编程语言 去评论

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/article/2590.html,未经允许,请勿转载。