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


当前回答

我正在构建一个共享/动态库。它在Linux和*BSD上运行,但在Mac OS X上,完全相同的编译和链接命令会产生未解决的引用错误。有什么好处?

Mac OS X在内部与Linux和*BSD非常不同。对象/可执行文件格式为

在Linux和*BSD上,当构建共享库时,默认情况下允许未解析的引用。期望它们在加载时能够满足主可执行文件和/或其他共享库的要求。如果在加载时无法解析这些符号,则共享库将无法加载。

在Mac OS X上,构建动态库时,默认情况下不允许未解析的引用。如果希望在加载时解析引用,则需要显式启用未解析的引用。这是使用未定义的dynamic_lookup链接器标志完成的。

在构建可加载插件时,允许未解析的引用非常有用。

其他回答

不同的架构

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

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,因为当前搜索范围中不存在该定义。

使用带有代码运行程序扩展名和多个.c或.cpp文件的Visual Studio代码

所提供的Code Runner仅适用于具有单个源文件的编译程序。它不是为与多个源文件一起使用而设计的。您应该使用不同的扩展名,例如C/C++Makefile Project扩展名或CMake Tools扩展名,或者修复CodeRunner扩展名以处理多个文件,或者手动编辑.json配置文件。

使用链接器帮助诊断错误

大多数现代链接器都有一个冗长的选项,可以在不同程度上打印出来;

链接调用(命令行),关于链接阶段中包括哪些库的数据,库的位置,使用的搜索路径。

用于gcc和clang;通常会在命令行中添加-v-Wl、--verbose或-v-Wl和-v。更多详情请点击此处;

Linux ld手册页。LLVM链接器页。“GCC简介”第9章。

对于MSVC,/VERBOSE(特别是/VERBOSE:LIB)将添加到链接命令行。

/VERBOSE链接器选项上的MSDN页面。

我的例子:

头文件

class GameCharacter : public GamePart
{
    private:
        static vector<GameCharacter*> characterList;
...
}

.cpp文件:

vector<GameCharacter*> characterList;

这产生了“未定义”加载程序错误,因为“characterList”被声明为静态成员变量,但被定义为全局变量。

我加上这个是因为——虽然其他人在一长串需要注意的事情中列出了这个案例——但这个列表并没有给出示例。这是一个更值得寻找的例子,尤其是在C++中。

修复方法是向全局变量添加限定以定义静态数据成员:

vector<GameCharacter*> GameCharacter::characterList;

同时保持收割台相同。