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


当前回答

这是每个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下,您可以指定要在命令行中链接在一起的所有对象文件,或者一起编译实现文件。

g++ -o test objectFile1.o objectFile2.o -lLibraryName

-我。。。必须位于任何.o/.c/.cpp文件的右侧。

这里的libraryName只是库的裸名,没有特定于平台的添加。例如,在Linux上,库文件通常被称为libfoo.So,但您只能编写-lfo。在Windows上,相同的文件可能被称为foo.lib,但您将使用相同的参数。您可能需要添加目录,在该目录中可以使用-Lûdirectory›找到这些文件。确保不要在-l或-l后面写空格。

对于Xcode:添加用户标题搜索路径->添加库搜索路径->将实际的库引用拖放到项目文件夹中。

在MSVS下,添加到项目中的文件会自动将其对象文件链接在一起,并生成一个lib文件(常见用法)。要在单独的项目中使用符号,您需要需要在项目设置中包含lib文件。这是在项目财产的链接器部分的Input->Additional Dependencies中完成的。(指向lib文件的路径应为在Linker->General->Additional Library Directories中添加)当使用随lib文件提供的第三方库时,失败通常会导致错误。

还可能发生忘记将文件添加到编译中的情况,在这种情况下,不会生成对象文件。在gcc中,您可以将文件添加到命令行。在MSVS中,将文件添加到项目将使其自动编译(尽管文件可以手动从构建中单独排除)。

在Windows编程中,未链接必要库的标志是未解析符号的名称以__imp_开头。在文档中查找函数的名称,它应该指出您需要使用哪个库。例如,MSDN将信息放在名为“库”的部分中每个函数底部的框中。

不支持链接器脚本的GNUld包装器

一些.so文件实际上是GNU ld链接器脚本,例如libtbb.so文件是一个ASCII文本文件,其内容如下:

INPUT (libtbb.so.2)

一些更复杂的构建可能不支持这一点。例如,如果在编译器选项中包含-v,则可以看到mainwin gcc包装器mwdip丢弃要链接的库的详细输出列表中的链接器脚本命令文件。

cp libtbb.so.2 libtbb.so

或者可以用.so的完整路径替换-l参数,例如,代替-ltbb-do/home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtb.so.2

我的例子:

头文件

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

.cpp文件:

vector<GameCharacter*> characterList;

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

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

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

vector<GameCharacter*> GameCharacter::characterList;

同时保持收割台相同。

UNICODE定义不一致

Windows UNICODE构建时,TCHAR等被定义为wchar_t等。如果不使用UNICODE进行构建,则TCHAR被定义为char等。这些UNICODE和_UNICODE定义会影响所有“t”字符串类型;LPTSTR、LPCTSTR及其麋鹿。

构建一个定义了UNICODE的库,并试图将其链接到未定义UNICODE项目中,将导致链接器错误,因为TCHAR的定义将不匹配;char与wchar_t。

错误通常包括一个带有char或wchar_t派生类型的值的函数,这些类型也可能包括std::basic_string<>等。浏览代码中受影响的函数时,通常会引用TCHAR或std::basic_string<TCHAR>等。这是一个信号,表明代码最初用于UNICODE和多字节字符(或“窄”)构建。

要更正此问题,请使用UNICODE(和_UNICODE)的一致定义构建所有必需的库和项目。

这可以通过以下两种方式实现:;#定义UNICODE#定义UNICODE或在项目设置中;项目财产>常规>项目默认值>字符集或在命令行上;/DUNICODE/D-UNICODE

如果不打算使用UNICODE,请确保未设置定义,和/或在项目中使用了多字符设置,并始终应用。

不要忘记在“发布”和“调试”版本之间保持一致。

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