什么是未定义的引用/未解析的外部符号错误,我该如何解决?

时间:2012-09-24 22:27:41

标签: c++ linker-errors undefined-reference c++-faq unresolved-external

什么是未定义的引用/未解析的外部符号错误?什么是常见原因以及如何修复/预防它们?

随意编辑/添加您自己的。

37 个答案:

答案 0 :(得分:776)

按照 2.2 (credits to Keith Thompson for the reference)的指定,按照几个步骤编译C ++程序:

  

翻译语法规则的优先顺序由以下阶段 [见脚注] 指定。

     
      
  1. 物理源文件字符以实现定义的方式映射到基本源字符集   (引入行尾指标的换行符)if   必要。 [SNIP]
  2.   
  3. 删除反斜杠字符(\)后面紧跟一个新行字符的每个实例,将物理源代码行拼接到   形成逻辑源代码行。 [SNIP]
  4.   
  5. 源文件被分解为预处理标记(2.5)和空白字符序列(包括注释)。 [SNIP]
  6.   
  7. 执行预处理指令,扩展宏调用,并执行_Pragma一元运算符表达式。 [SNIP]
  8.   
  9. 字符文字或字符串文字中的每个源字符集成员,以及每个转义序列和通用字符名称   在字符文字或非原始字符串文字中,转换为   执行字符集的相应成员; [SNIP]
  10.   
  11. 连接相邻的字符串文字标记。
  12.   
  13. 分隔标记的空白字符不再重要。每个预处理令牌都转换为令牌。 (2.7)。该   产生的标记在语法和语义上进行分析   翻译为翻译单位。 [SNIP]
  14.   
  15. 翻译的翻译单元和实例化单元组合如下: [SNIP]
  16.   
  17. 解析所有外部实体引用。库组件被链接以满足对未定义的实体的外部引用   目前的翻译。所有这些翻译器输出都被收集到一个   程序映像,包含在其中执行所需的信息   执行环境。(强调我的)
  18.         

    [脚注] 实现必须表现得就像这些单独的阶段一样,尽管实际上不同的阶段可能会折叠在一起。

指定的错误发生在编译的最后阶段,通常称为链接。它基本上意味着你将一堆实现文件编译成目标文件或库,现在你想让它们一起工作。

假设您在a中定义了符号a.cpp。现在,b.cpp 声明该符号并使用它。在链接之前,它只是假设该符号已定义某处,但它还不关心在哪里。链接阶段负责查找符号并将其正确链接到b.cpp(实际上是与使用它的对象或库)。

如果您使用的是Microsoft Visual Studio,则会看到项目生成.lib个文件。它们包含导出符号表和导入符号表。导入的符号将根据您链接的库进行解析,并为使用该.lib(如果有)的库提供导出的符号。

其他编译器/平台也存在类似的机制。

Microsoft Visual Studio error LNK2001 symbolName 的常见错误消息为error LNK1120error LNK2019undefined reference to GCC

代码:

struct X
{
   virtual void foo();
};
struct Y : X
{
   void foo() {}
};
struct A
{
   virtual ~A() = 0;
};
struct B: A
{
   virtual ~B(){}
};
extern int x;
void foo();
int main()
{
   x = 0;
   foo();
   Y y;
   B b;
}

将使用 GCC 生成以下错误:

/home/AbiSfw/ccvvuHoX.o: In function `main':
prog.cpp:(.text+0x10): undefined reference to `x'
prog.cpp:(.text+0x19): undefined reference to `foo()'
prog.cpp:(.text+0x2d): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD1Ev[B::~B()]+0xb): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o: In function `B::~B()':
prog.cpp:(.text._ZN1BD0Ev[B::~B()]+0x12): undefined reference to `A::~A()'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1Y[typeinfo for Y]+0x8): undefined reference to `typeinfo for X'
/home/AbiSfw/ccvvuHoX.o:(.rodata._ZTI1B[typeinfo for B]+0x8): undefined reference to `typeinfo for A'
collect2: ld returned 1 exit status

以及 Microsoft Visual Studio 的类似错误:

1>test2.obj : error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)
1>test2.obj : error LNK2001: unresolved external symbol "int x" (?x@@3HA)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual __thiscall A::~A(void)" (??1A@@UAE@XZ)
1>test2.obj : error LNK2001: unresolved external symbol "public: virtual void __thiscall X::foo(void)" (?foo@X@@UAEXXZ)
1>...\test2.exe : fatal error LNK1120: 4 unresolved externals

常见原因包括:

答案 1 :(得分:165)

班级成员:

virtual析构函数需要实现。

声明析构函数pure仍然需要您定义它(与常规函数不同):

struct X
{
    virtual ~X() = 0;
};
struct Y : X
{
    ~Y() {}
};
int main()
{
    Y y;
}
//X::~X(){} //uncomment this line for successful definition

这是因为在隐式销毁对象时会调用基类析构函数,因此需要定义。

virtual方法必须实现或定义为纯。

这类似于没有定义的非virtual方法,并附加了推理 pure声明生成一个虚拟vtable,你可能会在不使用函数的情况下得到链接器错误:

struct X
{
    virtual void foo();
};
struct Y : X
{
   void foo() {}
};
int main()
{
   Y y; //linker error although there was no call to X::foo
}

为此,请将X::foo()声明为纯:

struct X
{
    virtual void foo() = 0;
};

virtual班级成员

即使未明确使用,也需要定义某些成员:

struct A
{ 
    ~A();
};

以下内容会产生错误:

A a;      //destructor undefined

实现可以在类定义本身内联:

struct A
{ 
    ~A() {}
};

或外面:

A::~A() {}

如果实现在类定义之外,但在标题中,则必须将方法标记为inline以防止多重定义。

如果使用,则需要定义所有使用过的成员方法。

一个常见的错误就是忘记取名这个名字:

struct A
{
   void foo();
};

void foo() {}

int main()
{
   A a;
   a.foo();
}

定义应该是

void A::foo() {}

static个数据成员必须在单个翻译单元中的课外定义:

struct X
{
    static int x;
};
int main()
{
    int x = X::x;
}
//int X::x; //uncomment this line to define X::x

可以为类定义中的整数或枚举类型的static const数据成员提供初始值设定项;但是,此成员的odr使用仍将需要如上所述的命名空间范围定义。 C ++ 11允许在类中为所有static const数据成员进行初始化。

答案 2 :(得分:103)

无法链接到适当的库/目标文件或编译实现文件

通常,每个翻译单元将生成一个目标文件,其中包含该翻译单元中定义的符号的定义。 要使用这些符号,您必须链接这些目标文件。

gcc 下,您将指定要在命令行中链接在一起的所有目标文件,或者一起编译实现文件。

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

这里的libraryName只是库的名称,没有特定于平台的添加。所以例如Linux库文件通常称为libfoo.so,但您只需编写-lfoo。在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将信息放在每个函数底部的一个框中,名为“Library”。

答案 3 :(得分:94)

已声明但未定义变量或函数。

典型的变量声明是

extern int x;

由于这只是一个声明,因此需要单一定义。相应的定义是:

int x;

例如,以下内容会生成错误:

extern int x;
int main()
{
    x = 0;
}
//int x; // uncomment this line for successful definition

类似的评论适用于功能。在不定义函数的情况下声明函数会导致错误:

void foo(); // declaration only
int main()
{
   foo();
}
//void foo() {} //uncomment this line for successful definition

请注意,您实现的功能与您声明的功能完全匹配。例如,您可能有不匹配的cv限定符:

void foo(int& x);
int main()
{
   int x;
   foo(x);
}
void foo(const int& x) {} //different function, doesn't provide a definition
                          //for void foo(int& x)

其他不匹配的例子包括

  • 在一个名称空间中声明的函数/变量,在另一个名称空间中定义。
  • 声明为类成员的函数/变量,定义为全局(反之亦然)。
  • 函数返回类型,参数编号和类型以及调用约定都不完全一致。

编译器的错误消息通常会为您提供已声明但从未定义的变量或函数的完整声明。将其与您提供的定义进行比较。 确保每个细节都匹配。

答案 4 :(得分:78)

指定相互依赖的链接库的顺序是错误的。

库的链接顺序如果库彼此依赖则很重要。通常,如果库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

再次重复, DOES 的顺序很重要!

答案 5 :(得分:69)

什么是"未定义的引用/未解析的外部符号"

我将尝试解释什么是"未定义的引用/未解析的外部符号"。

  

注意:我使用的是g ++和Linux,所有的例子都是为了它

例如我们有一些代码

// src1.cpp
void print();

static int local_var_name; // 'static' makes variable not visible for other modules
int global_var_name = 123;

int main()
{
    print();
    return 0;
}

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
//extern int local_var_name;

void print ()
{
    // printf("%d%d\n", global_var_name, local_var_name);
    printf("%d\n", global_var_name);
}

制作目标文件

$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o

在汇编程序阶段之后,我们有一个目标文件,其中包含要导出的任何符号。 看看符号

$ readelf --symbols src1.o
  Num:    Value          Size Type    Bind   Vis      Ndx Name
     5: 0000000000000000     4 OBJECT  LOCAL  DEFAULT    4 _ZL14local_var_name # [1]
     9: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 global_var_name     # [2]

我已拒绝输出中的某些行,因为它们无关紧要

因此,我们会看到要导出的符号。

[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable

src2.cpp什么都没输出,我们看不到它的符号

链接我们的目标文件

$ g++ src1.o src2.o -o prog

并运行它

$ ./prog
123

链接器会看到导出的符号并将其链接起来。现在我们尝试取消注释src2.cpp中的行,就像这里一样

// src2.cpp
extern "C" int printf (const char*, ...);

extern int global_var_name;
extern int local_var_name;

void print ()
{
    printf("%d%d\n", global_var_name, local_var_name);
}

并重建目标文件

$ g++ -c src2.cpp -o src2.o

确定(没有错误),因为我们只构建目标文件,链接尚未完成。 尝试链接

$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status

之所以发生这种情况是因为我们的local_var_name是静态的,即它对其他模块不可见。 现在更深刻了。获取转换阶段输出

$ g++ -S src1.cpp -o src1.s

// src1.s
look src1.s

    .file   "src1.cpp"
    .local  _ZL14local_var_name
    .comm   _ZL14local_var_name,4,4
    .globl  global_var_name
    .data
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; assembler code, not interesting for us
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
    .section    .note.GNU-stack,"",@progbits

因此,我们已经看到local_var_name没有标签,这就是链接器没有找到它的原因。但我们是黑客:)我们可以解决它。在文本编辑器中打开src1.s并更改

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789

即。你应该在下面

    .file   "src1.cpp"
    .globl  local_var_name
    .data
    .align 4
    .type   local_var_name, @object
    .size   local_var_name, 4
local_var_name:
    .long   456789
    .globl  global_var_name
    .align 4
    .type   global_var_name, @object
    .size   global_var_name, 4
global_var_name:
    .long   123
    .text
    .globl  main
    .type   main, @function
main:
; ...

我们更改了local_var_name的可见性,并将其值设置为456789。 尝试从中构建目标文件

$ g++ -c src1.s -o src2.o

好的,请参阅readelf输出(符号)

$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

现在local_var_name有Bind GLOBAL(是LOCAL)

链路

$ g++ src1.o src2.o -o prog

并运行它

$ ./prog 
123456789

好的,我们破解它:)

因此,结果是"未定义的参考/未解决的外部符号错误"当链接器无法在目标文件中找到全局符号时发生。

答案 6 :(得分:64)

符号在C程序中定义,并在C ++代码中使用。

函数(或变量)void foo()是在C程序中定义的,并且您尝试在C ++程序中使用它:

void foo();
int main()
{
    foo();
}

C ++链接器需要修改名称,因此您必须将该函数声明为:

extern "C" void foo();
int main()
{
    foo();
}

等效地,不是在C程序中定义,而是在C ++中定义函数(或变量)void foo(),但使用C链接:

extern "C" void foo();

并且您尝试在具有C ++链接的C ++程序中使用它。

如果整个库包含在头文件中(并编译为C代码);包含将需要如下;

extern "C" {
    #include "cheader.h"
}

答案 7 :(得分:62)

如果其他所有方法都失败了,请重新编译。

我最近只能通过重新编译有问题的文件来摆脱Visual Studio 2012中未解决的外部错误。当我重建时,错误就消失了。

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

答案 8 :(得分:54)

跨modules / dll(特定于编译器)错误地导入/导出方法/类。

MSVS要求您使用__declspec(dllexport)__declspec(dllimport)指定要导出和导入的符号。

这种双重功能通常通过使用宏来获得:

#ifdef THIS_MODULE
#define DLLIMPEXP __declspec(dllexport)
#else
#define DLLIMPEXP __declspec(dllimport)
#endif

THIS_MODULE只能在导出函数的模块中定义。那样,声明:

DLLIMPEXP void foo();

扩展为

__declspec(dllexport) void foo();

并告诉编译器导出该函数,因为当前模块包含其定义。将声明包含在不同的模块中时,它将扩展为

__declspec(dllimport) void foo();

并告诉编译器该定义位于您链接的一个库中(另请参见 1))。

您可以类似导入/导出类:

class DLLIMPEXP X
{
};

答案 9 :(得分:51)

这是每个VC ++程序员一次又一次看到的最令人困惑的错误消息之一。让我们先把事情弄清楚。

:一种。什么是符号? 简而言之,符号就是一个名称。它可以是变量名,函数名,类名,typedef名,或者除了那些属于C ++语言的名称和符号之外的任何名称。它由用户定义或由依赖库(另一个用户定义的)引入。

<强> B中。什么是外部的? 在VC ++中,每个源文件(.cpp,.c等)都被视为转换单元,编译器一次编译一个单元,并为当前转换单元生成一个目标文件(.obj)。 (请注意,此源文件包含的每个头文件都将被预处理,并将被视为此翻译单元的一部分)翻译单元中的所有内容都被视为内部,其他所有内容都被视为外部。在C ++中,您可以使用extern__declspec (dllimport)等关键字引用外部符号。

<强>℃。什么是“决心”? Resolve是一个链接时间术语。在链接时,链接器尝试在内部无法找到其定义的目标文件中查找每个符号的外部定义。此搜索过程的范围包括:

  • 在编译时生成的所有目标文件
  • 显式或隐式的所有库(.lib) 指定为此建筑应用程序的附加依赖项。

此搜索过程称为“解决”。

<强> d。最后,为什么没有解析外部符号? 如果链接器无法在内部找到没有定义的符号的外部定义,则会报告未解析的外部符号错误。

<强>电子。 LNK2019的可能原因:未解决的外部符号错误。 我们已经知道这个错误是由于链接器未能找到外部符号的定义,可能的原因可以排序为:

  1. 定义存在
  2. 例如,如果我们在a.cpp中定义了一个名为foo的函数:

    int foo()
    {
        return 0;
    }
    

    在b.cpp中我们想调用函数foo,所以我们添加

    void foo();
    

    声明函数foo(),并在另一个函数体中调用它,比如bar()

    void bar()
    {
        foo();
    }
    

    现在,当您构建此代码时,您将收到LNK2019错误,抱怨foo是一个未解析的符号。在这种情况下,我们知道foo()在a.cpp中有它的定义,但与我们调用的那个不同(不同的返回值)。这就是定义存在的情况。

    1. 定义不存在
    2. 如果我们想要调用库中的某些函数,但导入库未添加到项目设置的附加依赖项列表(来自:Project | Properties | Configuration Properties | Linker | Input | Additional Dependency)中。现在链接器将报告LNK2019,因为当前搜索范围中不存在定义。

答案 10 :(得分:51)

模板实现不可见。

非专业化模板必须使其定义对使用它们的所有翻译单元都可见。这意味着您无法分离模板的定义 到一个实现文件。如果必须将实现分开,通常的解决方法是使用impl文件包含在标题的末尾, 声明模板。常见的情况是:

template<class T>
struct X
{
    void foo();
};

int main()
{
    X<int> x;
    x.foo();
}

//differentImplementationFile.cpp
template<class T>
void X<T>::foo()
{
}

要解决此问题,您必须将X::foo的定义移至标题文件或使用它的翻译单元可见的某个位置。

可以在实现文件中实现专用模板,并且实现不必是可见的,但必须事先声明专门化。

有关进一步说明和其他可能的解决方案(显式实例化),请参阅this question and answer

答案 11 :(得分:38)

WinMain@16或类似'不寻常' main()入口点参考的未定义引用(特别是对于)。

您可能错过了使用实际IDE选择正确的项目类型。 IDE可能想要绑定例如Windows应用程序投射到此类入口点函数(如上面缺少的参考中所指定),而不是常用的int main(int argc, char** argv);签名。

如果您的IDE支持 Plain Console Projects ,您可能希望选择此项目类型,而不是Windows应用程序项目。


以下是现实世界问题中更详细处理的case1case2

答案 12 :(得分:35)

此外,如果您使用第三方库,请确保您拥有正确的32/64位二进制文​​件

答案 13 :(得分:34)

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

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

除了包含库目录的库路径外,这应该是库的全名。

答案 14 :(得分:32)

需要针对新工具集版本更新Visual Studio NuGet包

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

正确的解决方案是希望开发人员发布更新的软件包然后进行升级,但是通过黑客攻击VS2013的额外设置,指向VS2012库文件,它对我有用。

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

答案 15 :(得分:31)

假设你有一个用c ++编写的大项目,它有一千个.cpp文件和一千个.h文件。让我们说该项目还依赖于十个静态库。让我们说我们在Windows上,我们在Visual Studio 20xx中构建我们的项目。当您按Ctrl + F7 Visual Studio开始编译整个解决方案时(假设我们在解决方案中只有一个项目)

编译的含义是什么?

  • Visual Studio搜索文件 .vcxproj 并开始编译扩展名为.cpp的每个文件。编译顺序未定义。因此,您不能假设首先编译main.cpp文件
  • 如果.cpp文件依赖于其他.h文件以查找符号 可能会也可能不会在文件.cpp
  • 中定义
  • 如果存在一个.cpp文件,其中编译器找不到一个符号,编译器时间错误会引发消息无法找到符号x
  • 对于扩展名为.cpp的每个文件,都会生成一个目标文件.o,并且Visual Studio会将输出写入名为 ProjectName.Cpp.Clean.txt 的文件中,该文件包含必须存在的所有目标文件由链接器处理。

编译的第二步是由Linker完成的.Linker应合并所有目标文件并最终构建输出(可能是可执行文件或库)

关联项目的步骤

  • 解析所有目标文件并找到仅在头文件中声明的定义(例如:前面答案中提到的类的一个方法的代码,或者事件初始化静态变量,它是类中的成员)
  • 如果在目标文件中找不到一个符号,他也会在附加库中搜索。用于向项目添加新库配置属性 - &gt; VC ++目录 - &gt; 图书馆目录,此处您指定了用于搜索库的其他文件夹和配置属性 - &gt; 链接器 - &gt; 输入,用于指定库的名称。 - 如果链接器找不到您在一个.cpp中写入的符号,则会引发链接器时间错误,这可能听起来像 error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)

<强>观察

  1. 一旦链接器找到一个符号,他就不会在其他库中搜索它
  2. 链接库的顺序非常重要
  3. 如果链接器在一个静态库中找到外部符号,则在项目的输出中包含该符号。但是,如果库是共享的(动态的),则他不会在输出中包含代码(符号),但是运行时可能会发生崩溃
  4. 如何解决此类错误

    编译器时间错误:

    • 确保编写c ++项目语法正确。

    链接器时间错误

    • 定义您在头文件中声明的所有符号
    • 使用#pragma once允许编译器不包含一个标头(如果它已包含在编译的当前.cpp中)
    • 确保您的外部库不包含可能与您在标题文件中定义的其他符号冲突的符号
    • 使用模板时,请确保在头文件中包含每个模板函数的定义,以允许编译器为任何实例化生成适当的代码。

答案 16 :(得分:27)

编译器/ IDE中的错误

我最近遇到了这个问题,结果是it was a bug in Visual Studio Express 2013。我不得不从项目中删除源文件并重新添加它以克服错误。

如果您认为它可能是编译器/ IDE中的错误,请尝试执行此步骤:

  • 清理项目(某些IDE可以选择执行此操作,您也可以 通过删除目标文件来手动完成)
  • 尝试开始一个新项目, 复制原始源代码。

答案 17 :(得分:25)

使用链接器帮助诊断错误

大多数现代链接器都包含一个不同程度打印的详细选项;

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

对于gcc和clang;您通常会在命令行中添加-v -Wl,--verbose-v -Wl,-v。更多细节可以在这里找到;

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

答案 18 :(得分:24)

链接的.lib文件与.dll

相关联

我有同样的问题。说我有项目MyProject和TestProject。我已经有效地将MyProject的lib文件链接到了TestProject。但是,这个lib文件是在构建MyProject的DLL时生成的。另外,我没有包含MyProject中所有方法的源代码,只能访问DLL的入口点。

要解决此问题,我将MyProject构建为LIB,并将TestProject链接到此.lib文件(我将生成的.lib文件复制粘贴到TestProject文件夹中)。然后,我可以再次构建MyProject作为DLL。它正在编译,因为TestProject链接的lib确实包含MyProject中类中所有方法的代码。

答案 19 :(得分:17)

GNU ld的包装器,不支持链接器脚本

有些.so文件实际上是GNU ld linker scripts,例如libtbb.so文件是包含以下内容的ASCII文本文件:

INPUT (libtbb.so.2)

一些更复杂的构建可能不支持这一点。例如,如果在编译器选项中包含-v,则可以看到mainwin gcc wrapper mwdip丢弃了要链接的库的详细输出列表中的链接器脚本命令文件。一个简单的解决方法是替换链接器脚本输入命令文件,而不是文件的副本(或符号链接),例如

cp libtbb.so.2 libtbb.so

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

答案 20 :(得分:16)

交友模板......

给出带有友元操作符(或函数)的模板类型的代码片段;

template <typename T>
class Foo {
    friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};

operator<<被声明为非模板函数。对于与T一起使用的每种类型Foo,都需要有一个非模板operator<<。例如,如果声明了类型Foo<int>,那么必须有如下的运算符实现;

std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}

由于未实现,链接器无法找到它并导致错误。

要更正此问题,您可以在Foo类型之前声明模板运算符,然后将其声明为适当的实例。语法有点尴尬,但看起来如下;

// forward declare the Foo
template <typename>
class Foo;

// forward declare the operator <<
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&);

template <typename T>
class Foo {
    friend std::ostream& operator<< <>(std::ostream& os, const Foo<T>& a);
    // note the required <>        ^^^^
    // ...
};

template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&)
{
  // ... implement the operator
}

上面的代码限制了运算符与Foo的相应实例化的友谊,即operator<< <int>实例化仅限于访问Foo<int>实例化的私有成员。

替代品包括;

  • 允许友谊扩展到模板的所有实例化,如下所示;

    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
    
  • 或者,operator<<的实现可以在类定义中内联完成;

    template <typename T>
    class Foo {
        friend std::ostream& operator<<(std::ostream& os, const Foo& a)
        { /*...*/ }
        // ...
    };
    

注意,当运算符(或函数)的声明仅出现在类中时,该名称不能用于“正常”查找,仅用于参数相关查找,来自{{3} };

  

在类或类模板X中的友元声明中首先声明的名称成为X的最内层封闭命名空间的成员,但是无法进行查找(除了依赖于参数的查询,除非在此处使用匹配的声明)提供了命名空间范围...

cppreferencecppreference还有模板朋友的进一步阅读。

C++ FAQ

作为失败代码示例的附注; g ++警告如下

  

warning: friend declaration 'std::ostream& operator<<(...)' declares a non-template function [-Wnon-template-friend]

     

note: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here)

答案 21 :(得分:14)

您的链接在引用它们的对象文件之前使用库

  • 您正在尝试使用GCC工具链编译和链接您的程序。
  • 您的链接指定了所有必需的库和库搜索路径
  • 如果libfoo取决于libbar,则您的关联正确地将libfoo放在libbar之前。
  • 您的关联失败,并出现undefined reference to 某些错误。
  • 但所有未定义的某些都是在您拥有的头文件中声明的 #include d并且实际上是在您链接的库中定义的。

示例在C中。它们同样可以是C ++

涉及您自己构建的静态库的最小示例

<强> my_lib.c

#include "my_lib.h"
#include <stdio.h>

void hw(void)
{
    puts("Hello World");
}

<强> my_lib.h

#ifndef MY_LIB_H
#define MT_LIB_H

extern void hw(void);

#endif

<强> eg1.c

#include <my_lib.h>

int main()
{
    hw();
    return 0;
}

构建静态库:

$ gcc -c -o my_lib.o my_lib.c
$ ar rcs libmy_lib.a my_lib.o

您编译程序:

$ gcc -I. -c -o eg1.o eg1.c

您尝试将其与libmy_lib.a关联并失败:

$ gcc -o eg1 -L. -lmy_lib eg1.o 
eg1.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status

如果您在一个步骤中编译和链接,结果相同,如:

$ gcc -o eg1 -I. -L. -lmy_lib eg1.c
/tmp/ccQk1tvs.o: In function `main':
eg1.c:(.text+0x5): undefined reference to `hw'
collect2: error: ld returned 1 exit status

涉及共享系统库的最小示例,即压缩库libz

<强> eg2.c

#include <zlib.h>
#include <stdio.h>

int main()
{
    printf("%s\n",zlibVersion());
    return 0;
}

编译你的程序:

$ gcc -c -o eg2.o eg2.c

尝试将您的计划与libz关联,然后失败:

$ gcc -o eg2 -lz eg2.o 
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status

如果您一次编译和链接,则相同:

$ gcc -o eg2 -I. -lz eg2.c
/tmp/ccxCiGn7.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'
collect2: error: ld returned 1 exit status

示例2的变体涉及pkg-config

$ gcc -o eg2 $(pkg-config --libs zlib) eg2.o 
eg2.o: In function `main':
eg2.c:(.text+0x5): undefined reference to `zlibVersion'

你做错了什么?

在您要链接的目标文件和库的序列中 程序,您将库放在引用的目标文件之前 他们。您需要在引用的目标文件之后放置库 他们。

正确链接示例1:

$ gcc -o eg1 eg1.o -L. -lmy_lib

成功:

$ ./eg1 
Hello World

正确链接示例2:

$ gcc -o eg2 eg2.o -lz

成功:

$ ./eg2 
1.2.8

正确关联示例2 pkg-config变体:

$ gcc -o eg2 eg2.o $(pkg-config --libs zlib) 
$ ./eg2
1.2.8

解释

这里可以选择阅读。

默认情况下,GCC在您的发行版上生成的链接命令, 从左到右使用链接中的文件 命令行序列。当它发现文件引用某事时 并且不包含它的定义,以搜索定义 在右边的文件中。如果它最终找到一个定义,那么 参考已解决。如果任何引用仍未解决, 链接失败:链接器不向后搜索。

首先,示例1 ,包含静态库my_lib.a

静态库是目标文件的索引存档。当链接器 在链接序列中找到-lmy_lib,并指出这是指 到静态库./libmy_lib.a,它想知道你的程序是否正常 需要libmy_lib.a中的任何目标文件。

libmy_lib.a中只有对象文件,即my_lib.o,并且只有一件事定义了 在my_lib.o中,即函数hw

当且仅当它已经知道时,链接器才会确定您的程序需要my_lib.o 您的程序在其已有的一个或多个目标文件中引用hw 添加到程序中,并且没有添加任何目标文件 包含hw的定义。

如果确实如此,则链接器将从库中提取my_lib.o的副本 将其添加到您的程序中。然后,您的程序包含hw的定义,所以 它对hw的引用是已解决

当您尝试链接程序时:

$ gcc -o eg1 -L. -lmy_lib eg1.o

链接器在看到 eg1.o 时未将 -lmy_lib 添加到程序 eg1.o。因为在那时,它还没有看到hw。 您的程序尚未引用eg1.o:它 还没有任何引用 ,因为它所做的所有引用 在my_lib.o

因此,链接器不会向程序添加libmy_lib.a,也没有进一步 用于eg1.o

接下来,它找到hw,并将其添加为程序。中的一个目标文件 链接序列始终添加到程序中。现在,该计划使 对hw的引用,不包含hw的定义;但 链接序列中没有任何东西可以提供缺失 定义。对libz的引用最终未解析,并且链接失败。

其次,示例2 ,共享库main

共享库不是目标文件或类似内容的存档。它&#39; S 更像是一个没有gcc功能的程序 而是公开它定义的多个其他符号,以便其他符号 程序可以在运行时使用它们。

今天许多Linux发行版都配置了他们的GCC工具链,以便其语言驱动程序(g++gfortranld等) 指示系统链接器(-lz)在按需的基础上链接共享库。 你有一个发行版。

这意味着当链接器在链接序列中找到/usr/lib/x86_64-linux-gnu/libz.so时,并指出它是指的 对于共享库(比如说)libz,它想知道它添加到您的程序中的任何尚未定义的引用是否具有由libz

导出的定义

如果是这样,那么链接器将libz复制任何块。 将它们添加到您的程序中;相反,它只是医生你的程序的代码 这样: -

  • 在运行时,系统程序加载程序会将libz的副本加载到 与程序相同的过程,无论何时加载程序副本,运行它。

  • 在运行时,只要程序引用了定义的内容 libz,该引用使用libz副本导出的定义 同样的过程。

您的程序只想引用zlibVersion导出定义的一件事, 即函数eg2.c,在libz中只引用一次。 如果链接器将该引用添加到您的程序,然后找到该定义 由gcc -o eg2 -lz eg2.o 导出,引用已解决

但是当你尝试链接程序时:

-lz

事件的顺序与示例1的方式相同。 在链接器找到eg2.o时,有 no 引用任何内容 在该计划中:它们全都在libz,尚未见过。所以 链接器决定它对eg2.o没有用处。当它到达zlibVersion时,将其添加到程序中, 然后对pkg-config进行未定义的引用,链接序列完成; 该引用尚未解决,并且链接失败。

最后,示例2的gcc -o eg2 $(pkg-config --libs zlib) eg2.o 变体现在有了明显的解释。 壳膨胀后:

gcc -o eg2 -lz eg2.o

变为:

gcc -o eg2 -lz eg2.o

这又是例2。

我可以在示例1中重现该问题,但不能在示例2中重现

联系:

-lfoo

适合你!

(或者说:这个联系对你来说很好,比如Fedora 23,但在Ubuntu 16.04上失败了)

那是因为链接工作的发行版是其中之一 不会将其GCC工具链配置为根据需要链接共享库

在当天,类似unix的系统链接静态和共享是正常的 不同规则的图书馆。链接序列中的静态文库被链接 在示例1中解释的 as-needed 基础上,但共享库是无条件链接的。

这种行为在链接时很经济,因为链接器不需要思考 程序是否需要共享库:如果它是共享库, 链接它。大多数链接中的大多数库都是共享库。但也有缺点: -

  • 运行时是不经济的,因为它可能导致共享库 即使不需要,也会加载一个程序。

  • 静态和共享库的不同链接规则可能会令人困惑 不熟练的程序员,可能不知道他们的联系中是否/some/where/libfoo.a 将解析为/some/where/libfoo.so$ gcc -o eg1 -I. -L. -lmy_lib eg1.c , 并且可能无法理解共享库和静态库之间的区别 反正。

这种权衡导致了今天的分裂局面。一些发行版有 更改了共享库的GCC链接规则,以便根据需要 原则适用于所有图书馆。一些发行版一直坚持旧版 方式。

为什么即使我同时编译和链接,我仍然会遇到这个问题?

如果我这样做:

eg1.c

肯定gcc必须首先编译libmy_lib.a,然后链接结果 gcc的目标文件。那怎么能不知道那个目标文件呢 在进行链接时需要吗?

因为使用单个命令进行编译和链接不会改变 连锁序列的顺序。

当你运行上面的命令时,$ gcc -I. -c -o eg1.o eg1.c $ gcc -o eg1 -L. -lmy_lib eg1.o 指出你想要编译+ 连锁。所以在幕后,它会生成一个编译命令,然后运行 它,然后生成一个连接命令,然后运行它,好像已经运行了 两个命令:

eg1.o

因此,如果您执行运行这两个命令,链接就会失败。该 只有你在失败时注意到的差异是gcc生成了一个 编译+链接案例中的临时目标文件,因为您没有告诉它 使用/tmp/ccQk1tvs.o: In function `main' 。我们看到:

eg1.o: In function `main':

而不是:

{{1}}

另见

The order in which interdependent linked libraries are specified is wrong

将相互依赖的库置于错误的顺序只是一种方式 您可以在其中获取需要即将发生的事情定义的文件 后来在链接中比提供定义的文件。把图书放在。之前 引用它们的目标文件是造成同样错误的另一种方法。

答案 22 :(得分:13)

UNICODE定义不一致

使用TCHAR等构建的Windows UNICODE构建定义为wchar_t等。如果未构建UNICODE定义为构建,TCHAR定义为{{1}这些charUNICODE定义会影响所有"T" string types; _UNICODELPTSTR和他们的麋鹿。

构建一个定义了LPCTSTR的库并尝试在未定义UNICODE的项目中链接它将导致链接器错误,因为UNICODE的定义不匹配; TCHARchar

错误通常包含一个函数,其值为wchar_tchar派生类型,这些函数也可能包括wchar_t等。在代码中浏览受影响的函数时,通常会引用std::basic_string<>TCHAR等。这是一个告诉标志,该代码最初用于UNICODE和Multi -Byte Character(或“narrow”)build。

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

  1. 可以使用;

    _UNICODE
  2. 或者在项目设置中;

      

    项目属性&gt;一般&gt;项目默认值&gt;字符集

  3. 或者在命令行上;

    #define UNICODE
    #define _UNICODE
    
  4. 替代方案也适用,如果不打算使用UNICODE,请确保未设置定义,和/或在项目中使用多字符设置并始终如一地应用。

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

答案 23 :(得分:13)

当您的包含路径不同时

当头文件及其关联的共享库(.lib文件)不同步时,可能会发生链接器错误。让我解释。

连接子如何工作?链接器通过比较它们的签名来匹配函数声明(在标头中声明)与其定义(在共享库中)。如果链接器找不到完全匹配的函数定义,则会出现链接器错误。

即使声明和定义似乎匹配,是否仍然可能会出现链接器错误?是!它们在源代码中看起来可能相同,但它实际上取决于编译器看到的内容。基本上你可能会遇到这样的情况:

// header1.h
typedef int Number;
void foo(Number);

// header2.h
typedef float Number;
void foo(Number); // this only looks the same lexically

请注意,即使两个函数声明在源代码中看起来都相同,但根据编译器它们实际上是不同的。

你可能会问这样的情况怎么结束? 包含路径当然!如果在编译共享库时,包含路径会导致header1.h并且您最终在自己的程序中使用header2.h,那么您将会抓住您的标题,想知道发生了什么(双关语)。

下面将解释现实世界中如何发生这种情况的一个例子。

进一步详细说明

我有两个项目:graphics.libmain.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(...);
}

轰!你得到一个链接器错误,你不知道为什么它失败了。原因是公共库使用相同的包含common_math.h的不同版本(我在示例中通过包含不同的路径使其显而易见,但它可能并不总是那么明显。可能包含路径不同在编译器设置中。)

请注意,在此示例中,链接器会告诉您它找不到draw(),而实际上您知道它显然是由库导出的。你可能会花几个小时挠头想知道出了什么问题。问题是,链接器看到不同的签名,因为参数类型略有不同。在示例中,就编译器而言,vec3在两个项目中都是不同的类型。这可能是因为它们来自两个略有不同的包含文件(可能包含文件来自两个不同版本的库)。

调试链接器

DUMPBIN是您的朋友,如果您使用的是Visual Studio。我相信其他编译器还有其他类似的工具。

过程如下:

  1. 请注意链接器错误中给出的奇怪的错位名称。 (例如,绘制@ graphics @ XYZ)。
  2. 将导出的符号从库中转储到文本文件中。
  3. 搜索感兴趣的导出符号,并注意已损坏的名称不同。
  4. 注意为什么受损的名字最终会有所不同。您将能够看到参数类型不同,即使它们在源代码中看起来相同。
  5. 他们与众不同的原因。在上面给出的示例中,由于包含不同的文件,它们是不同的。
  6. [1]项目是指一组链接在一起产生库或可执行文件的源文件。

    编辑1:重写第一部分以便于理解。如果需要修复其他内容,请在下方发表评论以告诉我。谢谢!

答案 24 :(得分:12)

清理并重建

A&#34;清洁&#34;构建可以删除死木&#34;可能会留在以前的构建,失败的构建,不完整的构建和其他构建系统相关的构建问题。

通常,IDE或构建将包含某种形式的&#34; clean&#34;函数,但可能没有正确配置(例如在手动makefile中)或可能失败(例如,中间或结果二进制文件是只读的。)

一旦&#34;清洁&#34;已完成,验证&#34;清洁&#34;已成功完成所有生成的中间文件(例如自动化的makefile)已成功删除。

这个过程可以被视为最后的手段,但往往是一个很好的第一步;特别是如果最近添加了与错误相关的代码(本地或从源存储库)。

答案 25 :(得分:7)

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

对于来自C的人来说,在C ++中,全局const变量具有内部(或静态)链接可能会令人感到意外。在C中情况并非如此,因为所有全局变量都隐含extern(即缺少static关键字时)。

示例:

// 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;

或者,可以使用显式const

在file1.cpp中声明extern变量

答案 26 :(得分:3)

尽管这是一个很老的问题,有多个被接受的答案,但我还是想分享如何解决晦涩的“未定义的引用”错误。

库的不同版本

我使用别名来引用std::filesystem::path:自C ++ 17起,文件系统就位于标准库中,但是我的程序需要也要在C ++ 14中进行编译,所以我决定使用变量别名:

#if (defined _GLIBCXX_EXPERIMENTAL_FILESYSTEM) //is the included filesystem library experimental? (C++14 and newer: <experimental/filesystem>)
using path_t = std::experimental::filesystem::path;
#elif (defined _GLIBCXX_FILESYSTEM) //not experimental (C++17 and newer: <filesystem>)
using path_t = std::filesystem::path;
#endif

假设我有三个文件:main.cpp,file.h,file.cpp:

  • file.h #include的<< strong> experimental :: filestystem >并包含上面的代码
  • file.cpp ,file.h的实现,#include的“ file.h
  • main.cpp #include的<< strong> filestystem >和“ file.h

请注意main.cpp和file.h中使用的不同库。由于main.cpp在 filestystem >之后#include“ file.h ”,因此所使用的文件系统版本为 C ++ 17版本。我以前使用以下命令来编译程序:

$ g++ -g -std=c++17 -c main.cpp->将main.cpp编译为main.o
$ {g++ -g -std=c++17 -c file.cpp->将file.cpp和file.h编译为file.o
$ {g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs->链接main.o和file.o

这样,需要path_t 的file.o中包含的和main.o中使用的任何函数都会给出“未定义的引用”错误,因为 main .o 指的是 std::filesystem::path ,但 file.o 是指 std::experimental::filesystem::path

解决方案

要解决此问题,我只需要将file.h中的更改为

答案 27 :(得分:2)

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

gcc的默认行为是所有符号均可见。但是,当使用选项-fvisibility=hidden构建翻译单元时,只有标记为__attribute__ ((visibility ("default")))的功能/符号在结果共享对象的外部。

您可以通过调用以下命令来检查所需符号是否为外部符号:

# -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,将用定义来标记公共功能,例如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

更多information about visibility可以在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

答案 28 :(得分:1)

当我们在程序中对对象名称(类,函数,变量等)进行引用,并且链接器在尝试搜索时找不到其定义时,发生“ 未定义引用”错误在所有链接的目标文件和库中使用它。

因此,当链接器找不到链接对象的定义时,它将发出“未定义引用”错误。从定义上可以清楚地看出,此错误发生在链接过程的后期。导致“未定义引用”错误的原因有多种。

某些可能的原因(更频繁):

#1)没有为对象提供定义

这是导致“未定义引用”错误的最简单原因。程序员只是忘记了定义对象。

请考虑以下C ++程序。在这里,我们只指定了函数原型,然后在主要函数中使用了它。

#include <iostream>
int func1();
int main()
{
     
    func1();
}

输出:

main.cpp:(.text+0x5): undefined reference to 'func1()'
collect2: error ld returned 1 exit status

因此,当我们编译该程序时,将发出链接器错误,指出“未定义对'func1()'的引用”。

为了消除此错误,我们通过提供函数func1的定义按如下方式纠正程序。现在,程序会提供适当的输出。

#include <iostream>
using namespace std;
int func1();
 
int main()
{
     
    func1();
}
int func1(){
    cout<<"hello, world!!";
}

输出:

hello, world!!

#2)使用的对象的定义错误(签名不匹配)

另一个导致“未定义引用”错误的原因是当我们指定错误的定义时。我们在程序中使用任何对象,其定义有所不同。

请考虑以下C ++程序。在这里,我们对func1()进行了调用。它的原型是int func1()。但是其定义与原型不符。如我们所见,该函数的定义包含该函数的参数。

因此,在编译程序时,由于原型和函数调用匹配,编译成功。但是,当链接器尝试将函数调用与其定义链接时,它会发现问题并以“未定义引用”的形式发出错误。

#include <iostream>
using namespace std;
int func1();
int main()
{
     
    func1();
}
int func1(int n){
    cout<<"hello, world!!";
}

输出:

main.cpp:(.text+0x5): undefined reference to 'func1()'
collect2: error ld returned 1 exit status

为防止此类错误,我们只需交叉检查程序中所有对象的定义和用法是否匹配即可。

#3)对象文件未正确链接

此问题也可能导致“未定义参考”错误。在这里,我们可能有多个源文件,并且我们可能会独立编译它们。完成此操作后,对象将无法正确链接,并导致“未定义的引用”。

请考虑以下两个C ++程序。在第一个文件中,我们使用第二个文件中定义的“ print()”函数。当我们分别编译这些文件时,第一个文件为打印功能提供“未定义参考”,而第二个文件为主要功能提供“未定义参考”。

int print();
int main()
{
    print();
}

输出:

main.cpp:(.text+0x5): undefined reference to 'print()'
collect2: error ld returned 1 exit status

int print() {
    return 42;
}

输出:

(.text+0x20): undefined reference to 'main'
collect2: error ld returned 1 exit status

解决此错误的方法是同时编译两个文件(例如,使用g ++)。

除了已经讨论的原因之外,由于以下原因,还会出现“未定义参考”。

#4)项目类型错误

当我们在Visual Studio之类的C ++ IDE中指定错误的项目类型并尝试执行该项目所不希望做的事情时,我们将获得“未定义的引用”。

#5)没有库

如果程序员没有正确或完全忘记指定库路径来指定它,那么对于程序从库中使用的所有引用,我们都会得到一个“未定义的引用”。

#6)依赖文件未编译

程序员必须确保我们预先编译了项目的所有依赖项,以便在我们编译项目时,编译器会找到所有依赖项并成功进行编译。如果缺少任何依赖项,则编译器会给出“未定义的引用”。

除上述原因外,在许多其他情况下也会发生“未定义参考”错误。但最重要的是,程序员犯了错,为了防止出现此错误,应予以纠正。

答案 29 :(得分:0)

不同的体系结构

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

library machine type 'x64' conflicts with target machine type 'X86'

在这种情况下,这意味着可用的符号用于与您要编译的符号不同的体系结构。

在Visual Studio上,这是由于错误的“平台”造成的,您需要选择正确的库或安装正确的库版本。

在Linux上,这可能是由于库文件夹错误(例如,使用lib而不是lib64造成的)。

在MacOS上,可以选择将两个体系结构都放在同一文件中。链接可能希望两个版本都存在,但只有一个版本存在。也可能是错误的lib / lib64文件夹在拾取库的文件夹中出现的问题。

答案 30 :(得分:0)

函数或类方法是在带有inline说明符的源文件中定义的。

一个例子:-

main.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;
};

gum.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)定义为inline,则 编译器将通过以下方式内联gum(如果选择这样做):-

  • 未发布gum的任何唯一定义,因此
  • 不发出链接程序可以用来引用gum的定义的任何符号,而是
  • gum的已编译主体的内联副本替换对gum的所有调用。

因此,如果您在源文件gum中内联定义gum.cpp,则它是 编译为目标文件gum.o,其中对gum的所有调用均内联 并且没有定义链接器可用来引用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的每个源文件中看到其定义时,才可以将inline定义为gum。这意味着其内联定义需要存在于您在每个源文件中 include header 文件中 您可以在其中编译gum的情况下进行编译。请执行以下两项操作之一:

要么不内联定义

从源文件定义中删除inline指定符:

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

gum.h(2)

#pragma once
#include <iostream>

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

现在我们不需要foo.cppgum.cpp

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

答案 31 :(得分:0)

  • 可以使用g ++ 44(Red Hat 4.4.7-8)按原样编译代码

Ubuntu具有Debian补丁的gcc-4.4 / g ++-4.4 :此代码(以及其他一些ns2代码/补丁)也不好。

Ubuntu:mpolsr_umolsr-v1_ns235.patchhttps://drive.google.com/file/d/0B7S...ew?usp=sharing(使用mpolsr代码创建于2017年,无变化。)

tar xvf ns-allinone-2.35_gcc5.tar.gz

https://drive.google.com/file/d/0B7S...ew?usp=sharing

cd ns-allinone-2.35/
patch -p0 < mpolsr_umolsr-v1_ns235.patch  // umolsr version v1.0 is used
./install
              // Stops with MPOLSR.cc
cd ns-2.35/
              // Edit the Makefile line 37 to CPP = g++34 , and run make
make
              // When 'make' stops with an mdart/* error, change to CPP = g++-4.4
make

gcc34 Ubuntu https://drive.google.com/file/d/0B7S255p3kFXNRTkzQnRSNXZ6UVU/view?usp=sharing

g ++ 34 Ubuntu https://drive.google.com/file/d/0B7S255p3kFXNV3J3bnVoWGNWdG8/view?usp=sharing

答案 32 :(得分:0)

这个问题是通过在头文件中声明函数的原型而发生的:

int createBackground(VertexArray rVA, IntRect arena);

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

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

原型在定义中未在第一个参数中使用引用的事实导致了此问题。当我将两者都更改为正确匹配包含引用或不包含引用的内容时,此问题已解决。

干杯。

答案 33 :(得分:0)

我的例子:

头文件

df = pd.DataFrame({'a':['Bolivia (Plurinational State of)','Switzerland17']})

df['a'] = df['a'].str.replace('(\s*\(.*\)|\d+)','')
print (df)
             a
0      Bolivia
1  Switzerland

.cpp文件:

const string GMCHARACTER("character");
class GameCharacter : public GamePart
{
    private:
        string name;
        static vector<GameCharacter*> characterList;
    public:
        GameCharacter(cstring nm, cstring id) :
            GamePart(GMCHARACTER, id, TRUE, TRUE, TRUE),
            name(nm)
            { }
...
}

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

我添加此内容是因为-虽然其他人在要查找的一长串事情中列出了此案,但该列出并没有提供示例。这是一个寻找更多东西的示例,尤其是在C ++中。

答案 34 :(得分:0)

就我而言,语法是正确的,但是当一个类调用同一个 DLL 中的第二个类时,我遇到了那个错误。第二类的 CPP 文件在 Visual Studio 中有错误的 Properties->Item Type,在我的例子中它被设置为 C/C++ Header 而不是正确的 C/C++ compiler 所以编译器没有编译 CPP构建文件并导致错误 LNK2019

这是一个例子,假设语法正确,你应该通过更改属性中的项目类型来得到错误

//class A header file 
class ClassB; // FORWARD DECLERATION
class ClassA
{
public:
ClassB* bObj;
ClassA(HINSTANCE hDLL) ;
//  member functions
}
--------------------------------
//class A cpp file   
ClassA::ClassA(HINSTANCE hDLL) 
{
    bObj = new ClassB();// LNK2019 occures here 
    bObj ->somefunction();// LNK2019 occures here
}
/*************************/

//classB Header file
struct mystruct{}
class ClassB{
public:
ClassB();
mystruct somefunction();
}

------------------------------
//classB cpp file  
/* This is the file with the wrong property item type in visual studio --C/C++ Header-*/
ClassB::somefunction(){}
ClassB::ClassB(){}

答案 35 :(得分:0)

当您使用错误的编译器来构建程序时

如果您使用 gccclang 编译器套件,您应该根据您使用的语言使用正确的编译器驱动程序。使用 g++clang++ 编译和链接 C++ 程序。改用 gccclang 将导致对 C++ 标准库符号的引用未定义。示例:

$ gcc -o test test.cpp    
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccPv7MvI.o: warning: relocation against `_ZSt4cout' in read-only section `.text' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccPv7MvI.o: in function `main': test.cpp:(.text+0xe): undefined reference to `std::cout' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: test.cpp:(.text+0x13): undefined reference to `std::basic_ostream<char, std::char_traits<char> >& std::operator<< <std::char_traits<char> >(std::basic_ostream<char, std::char_traits<char> >&, char const*)' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: /tmp/ccPv7MvI.o: in function `__static_initialization_and_destruction_0(int, int)': 
test.cpp:(.text+0x43): undefined reference to `std::ios_base::Init::Init()' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: test.cpp:(.text+0x58): undefined reference to `std::ios_base::Init::~Init()' 
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/../../../../x86_64-pc-linux-gnu/bin/ld: warning: creating DT_TEXTREL in a PIE 
collect2: error: ld returned 1 exit status

答案 36 :(得分:-1)

当链接器在声明文件(即从头文件或声明文件)中找不到定义的任何定义时,将发生错误。在找不到实现时发生。

编译后,链接器会尝试从库或for虚拟函数或模板定义中查找实现。如果找不到,则会根据链接器体系结构发生这些错误。