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


当前回答

Microsoft提供了一个#pragma,以在链接时引用正确的库;

#pragma comment(lib, "libname.lib")

除了库路径(包括库的目录)之外,这应该是库的全名。

其他回答

编译器/IDE中的错误

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

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

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

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

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

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

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

我在头文件中声明函数的原型时遇到了这个问题:

int createBackground(VertexArray rVA,IntRect arena);

但随后使用具有第一个参数的引用在其他地方定义函数:

int createBackground(VertexArray&rVA,IntRect arena){}

原型没有在第一个参数中使用引用,而定义是,这一事实导致了这个问题。当我将两者都更改为正确匹配包含引用或不包含引用时,问题得到了解决。

干杯

函数或类方法在源文件中使用内联说明符定义。

例如:-

主.cpp

#include "gum.h"
#include "foo.h"

int main()
{
    gum();
    foo f;
    f.bar();
    return 0;
}

foo.h(1)

#pragma once

struct foo {
    void bar() const;
};

口香糖.h(1)

#pragma once

extern void gum();

foo.cpp(1)

#include "foo.h"
#include <iostream>

inline /* <- wrong! */ void foo::bar() const {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

gum.cpp(1)

#include "gum.h"
#include <iostream>

inline /* <- wrong! */ void gum()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

如果指定gum(类似地,foo::bar)在其定义中是内联的,那么编译器将通过以下方式内联gum(如果它选择):-

没有任何独特的口香糖定义,因此不发出任何符号,链接器可以通过该符号引用口香糖的定义,而是将所有对gum的调用替换为编译后的gum主体的内联副本。

因此,如果在源文件gum.cpp中内联定义gum,则编译为对象文件gum.o,其中所有对gum的调用都是内联的并且没有定义接头可以指代口香糖的符号。当你将gum.o与另一个对象文件(例如main.o)链接到程序中引用外部符号gum时,链接器无法解析这些参考文献。因此连杆失效:

编译:

g++ -c  main.cpp foo.cpp gum.cpp

链接:

$ g++ -o prog main.o foo.o gum.o
main.o: In function `main':
main.cpp:(.text+0x18): undefined reference to `gum()'
main.cpp:(.text+0x24): undefined reference to `foo::bar() const'
collect2: error: ld returned 1 exit status

如果编译器可以在调用gum的每个源文件中看到它的定义,则只能将gum定义为内联。这意味着它的内联定义需要存在于包含在每个源文件中的头文件中您可以在其中调用gum。做两件事之一:

要么不内联定义

从源文件定义中删除内联说明符:

foo.cpp(2)

#include "foo.h"
#include <iostream>

void foo::bar() const {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

gum.cpp(2)

#include "gum.h"
#include <iostream>

void gum()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

重新生成:

$ g++ -c  main.cpp foo.cpp gum.cpp
imk@imk-Inspiron-7559:~/develop/so/scrap1$ g++ -o prog main.o foo.o gum.o
imk@imk-Inspiron-7559:~/develop/so/scrap1$ ./prog
void gum()
void foo::bar() const

成功

或正确内联

头文件中的内联定义:

foo.h(2)

#pragma once
#include <iostream>

struct foo {
    void bar() const  { // In-class definition is implicitly inline
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};
// Alternatively...
#if 0
struct foo {
    void bar() const;
};
inline void foo::bar() const  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}
#endif

口香糖.h(2)

#pragma once
#include <iostream>

inline void gum() {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

现在我们不需要foo.cpp或gum.cpp:

$ g++ -c main.cpp
$ g++ -o prog main.o
$ ./prog
void gum()
void foo::bar() const

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

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