什么是未定义的参考/未解析的外部符号错误?常见原因是什么?如何解决/预防?


当前回答

不同的架构

您可能会看到这样的消息:

library machine type 'x64' conflicts with target machine type 'X86'

在这种情况下,这意味着可用符号用于不同于您正在编译的体系结构。

在Visual Studio上,这是由于错误的“平台”,您需要选择正确的平台或安装正确版本的库。

在Linux上,这可能是由于错误的库文件夹(例如,使用lib而不是lib64)。

在MacOS上,可以选择在同一文件中传送两种体系结构。可能是链接希望两个版本都存在,但只有一个版本存在。也可能是库所在的lib/lib64文件夹错误。

其他回答

这是每个VC++程序员一再看到的最令人困惑的错误消息之一。让我们先把事情弄清楚。

A.什么是符号?简而言之,符号就是名称。它可以是变量名、函数名、类名、typedef名,或者除了那些属于C++语言的名称和符号之外的任何名称和符号。它由用户定义或由依赖库(另一个用户定义的)引入。

B.什么是外部的?在VC++中,每个源文件(.cpp、.c等)都被视为一个翻译单元,编译器一次编译一个单元,并为当前翻译单元生成一个目标文件(.obj)。(请注意,此源文件包含的每个头文件都将被预处理,并将被视为此翻译单元的一部分)翻译单元中的所有内容都被视为内部内容,其他所有内容都视为外部内容。在C++中,可以使用关键字extern、__declspec(dllimport)等引用外部符号。

C.什么是“决心”?Resolve是一个链接时间术语。在链接时,链接器尝试为对象文件中无法在内部找到其定义的每个符号找到外部定义。此搜索过程的范围包括:

编译时生成的所有对象文件显式或隐式的所有库(.lib)指定为此生成应用程序的附加依赖项。

此搜索过程称为解析。

D.最后,为什么是未解决的外部符号?如果链接器找不到内部没有定义的符号的外部定义,则会报告“未解决的外部符号”错误。

E.LNK2019的可能原因:未解决的外部符号错误。我们已经知道,此错误是由于链接器未能找到外部符号的定义所致,可能的原因如下:

定义已存在

例如,如果我们在.cpp中定义了一个名为foo的函数:

int foo()
{
    return 0;
}

在b.cpp中,我们希望调用函数foo,因此我们添加

void foo();

要声明函数foo(),并在另一个函数体中调用它,请使用bar():

void bar()
{
    foo();
}

现在,当您构建此代码时,您将收到一个LNK2019错误,抱怨foo是一个未解析的符号。在本例中,我们知道foo()的定义在.cpp中,但与我们调用的定义不同(返回值不同)。这就是定义存在的情况。

定义不存在

如果我们想调用库中的某些函数,但导入库没有添加到项目设置的附加依赖项列表中(设置自:项目|财产|配置财产|链接器|输入|附加依赖项)。现在链接器将报告LNK2019,因为当前搜索范围中不存在该定义。

在链接共享库时,请确保未隐藏使用的符号。

gcc的默认行为是所有符号都可见。但是,当使用选项-fvisibility=hidden构建转换单元时,只有标记为__attribute__((可见性(“默认”))的函数/符号在生成的共享对象中是外部的。

您可以通过调用以下命令来检查要查找的符号是否为外部符号:

# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL 

隐藏/本地符号用小写符号类型的nm表示,例如t而不是代码段的“t”:

nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL

您还可以使用nm和选项-C来定义名称(如果使用了C++)。

与Windows DLL类似,可以使用define标记公共函数,例如DLL_public定义为:

#define DLL_PUBLIC __attribute__ ((visibility ("default")))

DLL_PUBLIC int my_public_function(){
  ...
}

大致对应于Windows的/MSVC版本:

#ifdef BUILDING_DLL
    #define DLL_PUBLIC __declspec(dllexport) 
#else
    #define DLL_PUBLIC __declspec(dllimport) 
#endif

有关可见性的更多信息可以在gcc wiki上找到。


当使用-fvisibility=hidden编译翻译单元时,生成的符号仍然具有外部链接(以大写符号类型显示,单位为nm),如果对象文件成为静态库的一部分,则可以毫无问题地用于外部链接。只有当对象文件链接到共享库中时,链接才会变为本地链接。

要查找对象文件中隐藏的符号,请运行:

>>> objdump -t XXXX.o | grep hidden
0000000000000000 g     F .text  000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g     F .text  000000000000000b .hidden HIDDEN_SYMBOL2

不同的架构

您可能会看到这样的消息:

library machine type 'x64' conflicts with target machine type 'X86'

在这种情况下,这意味着可用符号用于不同于您正在编译的体系结构。

在Visual Studio上,这是由于错误的“平台”,您需要选择正确的平台或安装正确版本的库。

在Linux上,这可能是由于错误的库文件夹(例如,使用lib而不是lib64)。

在MacOS上,可以选择在同一文件中传送两种体系结构。可能是链接希望两个版本都存在,但只有一个版本存在。也可能是库所在的lib/lib64文件夹错误。

当您使用错误的编译器构建程序时

如果您使用的是gcc或clang编译器套件,则应根据所使用的语言使用正确的编译器驱动程序。使用g++或clang++编译和链接C++程序。改用gcc或clang将导致对C++标准库符号的引用未定义。例子:

$ gcc -o test test.cpp    
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccPv7MvI.o: warning: relocation against `_ZSt4cout' in read-only section `.text' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccPv7MvI.o: in function `main': test.cpp:(.text+0xe): undefined reference to `std::cout' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: test.cpp:(.text+0x13): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccPv7MvI.o: in function `__static_initialization_and_destruction_0(int, int)': 
test.cpp:(.text+0x43): undefined reference to `std::ios_base::Init::Init()' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: test.cpp:(.text+0x58): undefined reference to `std::ios_base::Init::~Init()' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: warning: creating DT_TEXTREL in a PIE 
collect2: error: ld returned 1 exit status

编译器/IDE中的错误

我最近遇到了这个问题,结果发现这是Visual Studio Express 2013中的一个错误。我不得不从项目中删除一个源文件,然后重新添加它以克服错误。

如果您认为这可能是编译器/IDE中的错误,请尝试以下步骤:

清理项目(一些IDE可以选择这样做,您也可以手动删除对象文件)尝试启动新项目,从原始代码复制所有源代码。