什么是未定义的参考/未解析的外部符号错误?常见原因是什么?如何解决/预防?
当前回答
指定相互依赖的链接库的顺序是错误的。
如果库相互依赖,则库的链接顺序也很重要。通常,如果库A依赖于库B,那么在链接器标志中,libA必须出现在libB之前。
例如:
// B.h
#ifndef B_H
#define B_H
struct B {
B(int);
int x;
};
#endif
// B.cpp
#include "B.h"
B::B(int xx) : x(xx) {}
// A.h
#include "B.h"
struct A {
A(int x);
B b;
};
// A.cpp
#include "A.h"
A::A(int x) : b(x) {}
// main.cpp
#include "A.h"
int main() {
A a(5);
return 0;
};
创建库:
$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o
ar: creating libB.a
a - B.o
编译:
$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out
再说一遍,顺序很重要!
其他回答
Microsoft提供了一个#pragma,以在链接时引用正确的库;
#pragma comment(lib, "libname.lib")
除了库路径(包括库的目录)之外,这应该是库的全名。
如果所有其他操作都失败,请重新编译。
最近,我只需重新编译有问题的文件,就可以消除Visual Studio 2012中未解决的外部错误。当我重新构建时,错误消失了。
当两个(或多个)库具有循环依赖关系时,通常会发生这种情况。库A尝试使用B.lib中的符号,库B尝试使用A.lib中的字符。两者都不存在。当您尝试编译A时,链接步骤将失败,因为它找不到B.lib。将生成A.lib,但不会生成dll。然后编译B,这将成功并生成B.lib。重新编译A现在可以工作了,因为现在找到了B.lib。
我在头文件中声明函数的原型时遇到了这个问题:
int createBackground(VertexArray rVA,IntRect arena);
但随后使用具有第一个参数的引用在其他地方定义函数:
int createBackground(VertexArray&rVA,IntRect arena){}
原型没有在第一个参数中使用引用,而定义是,这一事实导致了这个问题。当我将两者都更改为正确匹配包含引用或不包含引用时,问题得到了解决。
干杯
当包含路径不同时
当头文件及其关联的共享库(.lib文件)不同步时,可能会发生链接器错误。让我解释一下。
链接器是如何工作的?链接器通过比较函数声明(在头中声明)和函数定义(在共享库中)的签名来匹配它们。如果链接器找不到完全匹配的函数定义,则可能会出现链接器错误。
即使声明和定义似乎匹配,仍然可能出现链接器错误吗?对它们在源代码中看起来可能相同,但这实际上取决于编译器所看到的内容。基本上,你可能会遇到这样的情况:
// header1.h
typedef int Number;
void foo(Number);
// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically
注意,即使两个函数声明在源代码中看起来相同,但根据编译器的不同,它们确实不同。
你可能会问,一个人在这样的情况下是如何结束的?当然包括路径!如果在编译共享库时,include路径指向header1.h,而您最终在自己的程序中使用了header2.h,那么您将无法理解发生了什么(双关语)。
下面将解释这在现实世界中如何发生的一个示例。
通过示例进一步阐述
我有两个项目:graphics.lib和main.exe。两个项目都依赖common_math.h。假设库导出以下函数:
// graphics.lib
#include "common_math.h"
void draw(vec3 p) { ... } // vec3 comes from common_math.h
然后,您继续将库包含在您自己的项目中。
// main.exe
#include "other/common_math.h"
#include "graphics.h"
int main() {
draw(...);
}
繁荣你得到了一个链接器错误,你不知道它为什么会失败。原因是公共库使用相同includecommon_math.h的不同版本(我在本例中通过包含不同的路径来说明这一点,但可能并不总是那么明显。可能编译器设置中的包含路径不同)。
注意,在这个例子中,链接器会告诉你它找不到draw(),而实际上你知道它显然是由库导出的。你可以花几个小时挠头,想知道出了什么问题。问题是,链接器看到的签名不同,因为参数类型略有不同。在本例中,就编译器而言,vec3在两个项目中都是不同的类型。这可能是因为它们来自两个稍微不同的包含文件(可能包含文件来自库的两个不同版本)。
调试链接器
如果您正在使用Visual Studio,DUMPBIN是您的朋友。我相信其他编译器也有类似的工具。
过程如下:
注意链接器错误中给出的奇怪的损坏名称。(例如。draw@graphics@XYZ)。将库中导出的符号转储到文本文件中。搜索导出的感兴趣符号,并注意损坏的名称不同。请注意,为什么被弄乱的名字最终会不同。您将能够看到参数类型不同,即使它们在源代码中看起来相同。它们不同的原因。在上面给出的示例中,它们是不同的,因为包含文件不同。
[1] 我所说的项目是指一组链接在一起以生成库或可执行文件的源文件。
编辑1:改写第一节,使其更容易理解。请在下面评论,让我知道是否需要修复其他问题。谢谢
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,请确保未设置定义,和/或在项目中使用了多字符设置,并始终应用。
不要忘记在“发布”和“调试”版本之间保持一致。