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


当前回答

因为当涉及到链接器错误时,人们似乎都在关注这个问题,所以我将在这里添加这个问题。

GCC 5.2.0中出现链接器错误的一个可能原因是现在默认选择了新的libstdc++库ABI。

如果您在std::__cxx11命名空间或标记[abi:cx11]中获得了有关符号未定义引用的链接器错误,则可能表明您正在尝试将使用_GLIBCXX_USE_cxx11_abi宏的不同值编译的对象文件链接在一起。这通常发生在链接到使用旧版本GCC编译的第三方库时。如果无法使用新的ABI重建第三方库,则需要使用旧的ABI重新编译代码。

因此,如果在5.1.0之后切换到GCC时突然出现链接器错误,这将是一件值得检查的事情。

其他回答

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

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

常量变量声明/定义中缺少“extern”(仅限C++)

对于来自C语言的人来说,在C++中,全局常量变量具有内部(或静态)联系可能是一件令人惊讶的事情。在C中情况并非如此,因为所有全局变量都是隐式外部变量(即,当缺少静态关键字时)。

例子:

// file1.cpp
const int test = 5;    // in C++ same as "static const int test = 5"
int test2 = 5;

// file2.cpp
extern const int test;
extern int test2;

void foo()
{
 int x = test;   // linker error in C++ , no error in C
 int y = test2;  // no problem
}

正确的做法是使用头文件并将其包含在file2.cpp和file1.cpp中

extern const int test;
extern int test2;

或者,可以使用显式extern在file1.cpp中声明const变量

如果所有其他操作都失败,请重新编译。

最近,我只需重新编译有问题的文件,就可以消除Visual Studio 2012中未解决的外部错误。当我重新构建时,错误消失了。

当两个(或多个)库具有循环依赖关系时,通常会发生这种情况。库A尝试使用B.lib中的符号,库B尝试使用A.lib中的字符。两者都不存在。当您尝试编译A时,链接步骤将失败,因为它找不到B.lib。将生成A.lib,但不会生成dll。然后编译B,这将成功并生成B.lib。重新编译A现在可以工作了,因为现在找到了B.lib。

需要考虑的一些拼写错误:(我作为初学者经常遇到)

如果您使用的是类:请检查您是否没有在定义函数的cpp文件中的函数名之前忘记“classname::”。如果使用forward声明:请确保声明正确的类型。例如:如果要转发声明“结构”,请使用“结构”而不是“类”。

Visual Studio NuGet包需要更新以获得新的工具集版本

我在尝试将libpng与Visual Studio 2013链接时遇到了这个问题。问题是,包文件只有Visual Studio 2010和2012的库。

正确的解决方案是希望开发人员发布更新的软件包,然后进行升级,但这对我来说是有效的,因为我在VS2013的一个额外设置中进行了黑客攻击,指向了VS2012库文件。

我通过找到packagename\build\native\packagename.targets并在该文件中编辑了包(在解决方案目录中的packages文件夹中),复制了所有v110部分。我在条件字段中将v110更改为v120,只是非常小心地将文件名路径全部保留为v110。这只是允许Visual Studio 2013链接到2012年的库,在本例中,它起了作用。