什么是未定义的参考/未解决的外部符号错误,我该如何修复?

发布于 2025-01-22 21:20:52 字数 50 浏览 3 评论 0 原文

什么是未定义的参考/未解决的外部符号错误?什么是常见原因,我该如何解决和防止这些错误?

What are undefined reference/unresolved external symbol errors? What are common causes, and how do I fix and prevent these errors?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(30

马蹄踏│碎落叶 2025-01-29 21:20:52

说您有以下代码:

// a.cpp
int get() { return 0; }
// b.cpp
int get(); // usually, one doesn't write this directly, but gets these
           // declarations from included header files
int x = get();

编译 b.cpp 时,编译器只是假设 get()符号被定义为 虽然在乎哪里。链接阶段负责查找符号并正确链接从 a.cpp b.cpp 的对象文件。

如果 A.CPP 未定义 get ,则会获得一个链接器错误,说“未定义的参考”或“未解决的外部符号”。

C ++标准措辞

编译A C ++程序在,其中的最后一个相关:

9。所有外部实体参考均已解决。
库组件链接到满足当前翻译中未定义的实体的外部引用。
将所有此类翻译器输出收集到程序映像中,其中包含在其执行环境中执行所需的信息。

请参阅基思·汤普森(Keith Thompson)的答案有关这些阶段的摘要。

指定的错误发生在汇编的最后阶段,最常见地称为链接。这基本上意味着您将一堆源文件编译到对象文件或库中,现在您想让它们一起工作。

实践中的链接器错误

如果您使用的是Microsoft Visual Studio,则会看到项目生成 .lib 文件。这些包含一个导出的符号表和进口符号表。导入的符号针对您链接的库解决,并为使用该 .lib (如果有)的库提供导出的符号。

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

常见错误消息是错误LNK2001 错误lnk1120 错误lnk2019 for Microsoft Visual Studio and 未定义的参考 符号名称 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

常见原因

Say you have the following code:

// a.cpp
int get() { return 0; }
// b.cpp
int get(); // usually, one doesn't write this directly, but gets these
           // declarations from included header files
int x = get();

When compiling b.cpp, the compiler simply assumes that get() symbol was defined somewhere, but it doesn't yet care where. The linking phase is responsible for finding the symbol and correctly linking the object files produced from a.cpp and b.cpp.

If a.cpp didn't define get, you would get a linker error saying "undefined reference" or "unresolved external symbol".

C++ Standard Wording

Compiling a C++ program takes place in several phases specified in [lex.phases], the last of which is relevant:

9. All external entity references are resolved.
Library components are linked to satisfy external references to entities not defined in the current translation.
All such translator output is collected into a program image which contains information needed for execution in its execution environment.

See Keith Thompson's answer for a summary of these phases.

The specified errors occur during this last stage of compilation, most commonly referred to as linking. It basically means that you compiled a bunch of source files into object files or libraries, and now you want to get them to work together.

Linker Errors in Practice

If you're using Microsoft Visual Studio, you'll see that projects generate .lib files. These contain a table of exported symbols, and a table of imported symbols. The imported symbols are resolved against the libraries you link against, and the exported symbols are provided for the libraries that use that .lib (if any).

Similar mechanisms exist for other compilers/ platforms.

Common error messages are error LNK2001, error LNK1120, error LNK2019 for Microsoft Visual Studio and undefined reference to symbolName for GCC.

The code:

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

will generate the following errors with 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

and similar errors with 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

Common Causes

假装爱人 2025-01-29 21:20:52

类成员:

虚拟 destructor需要实现。

声明destructor纯净仍需要您定义它(与常规功能不同):

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

发生这种情况是因为当对象被隐式破坏时,因此调用了基础类破坏者,因此需要定义。

虚拟必须实现或定义为纯净的方法。

这类似于非定义的非虚拟方法,并增加了推理
纯声明会生成一个虚拟的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() us pure:

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

non- 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() {}

静态必须在单个翻译单元中定义类数据成员:

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

可以为A提供初始化器 static const 集体定义中积分或枚举类型的数据成员;但是,该成员的ODR使用仍需要如上所述的名称空间范围定义。 C ++ 11允许在所有 static const 数据成员的类中初始化。

Class members:

A pure virtual destructor needs an implementation.

Declaring a destructor pure still requires you to define it (unlike a regular function):

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

This happens because base class destructors are called when the object is destroyed implicitly, so a definition is required.

virtual methods must either be implemented or defined as pure.

This is similar to non-virtual methods with no definition, with the added reasoning that
the pure declaration generates a dummy vtable and you might get the linker error without using the function:

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

For this to work, declare X::foo() as pure:

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

Non-virtual class members

Some members need to be defined even if not used explicitly:

struct A
{ 
    ~A();
};

The following would yield the error:

A a;      //destructor undefined

The implementation can be inline, in the class definition itself:

struct A
{ 
    ~A() {}
};

or outside:

A::~A() {}

If the implementation is outside the class definition, but in a header, the methods have to be marked as inline to prevent a multiple definition.

All used member methods need to be defined if used.

A common mistake is forgetting to qualify the name:

struct A
{
   void foo();
};

void foo() {}

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

The definition should be

void A::foo() {}

static data members must be defined outside the class in a single translation unit:

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

An initializer can be provided for a static const data member of integral or enumeration type within the class definition; however, odr-use of this member will still require a namespace scope definition as described above. C++11 allows initialization inside the class for all static const data members.

別甾虛僞 2025-01-29 21:20:52

如果不链接到适当的库/对象文件或编译实现文件

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

GCC 下,您将指定要在命令行中链接在一起的所有对象文件,或将实现文件编译在一起。

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

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

Library名称这只是库的裸露名称,而没有平台特定的添加。因此,例如,Linux库文件通常称为 libfoo.so ,但您只能写 -lfoo 。在Windows上,同一文件可以称为 foo.lib ,但您将使用相同的参数。您可能必须添加可以使用 -L™目录› 找到这些文件的目录。确保在 -L -L 之后不编写空间。

对于 xcode :添加用户标头搜索路径 - >添加库搜索路径 - >将实际库参考拖放到项目文件夹中。

msvs 下,添加到项目的文件会自动将其对象文件链接在一起,并且将生成 lib 文件(常见用法)。要在一个单独的项目中使用符号,您将
需要在项目设置中包括 lib 文件。这是在项目属性的链接部分中完成的, INPUT->其他依赖关系。 ( lib 文件的路径应为
添加在链接器 - >一般 - >其他库目录)使用带有 lib 文件的第三方库时,通常会导致错误。

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

在Windows编程中,您没有链接必要库的讲故事符号是未解决的符号的名称以 __ Imp _ 开始。查找文档中功能的名称,应该说您需要使用哪个库。例如,MSDN将信息放在每个功能底部的框中​​,以“库”为节。

Failure to link against appropriate libraries/object files or compile implementation files

Commonly, each translation unit will generate an object file that contains the definitions of the symbols defined in that translation unit.
To use those symbols, you have to link against those object files.

Under gcc you would specify all object files that are to be linked together in the command line, or compile the implementation files together.

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

-l... must be to the right of any .o/.c/.cpp files.

The libraryName here is just the bare name of the library, without platform-specific additions. So e.g. on Linux library files are usually called libfoo.so but you'd only write -lfoo. On Windows that same file might be called foo.lib, but you'd use the same argument. You might have to add the directory where those files can be found using -L‹directory›. Make sure to not write a space after -l or -L.

For Xcode: Add the User Header Search Paths -> add the Library Search Path -> drag and drop the actual library reference into the project folder.

Under MSVS, files added to a project automatically have their object files linked together and a lib file would be generated (in common usage). To use the symbols in a separate project, you'd
need to include the lib files in the project settings. This is done in the Linker section of the project properties, in Input -> Additional Dependencies. (the path to the lib file should be
added in Linker -> General -> Additional Library Directories) When using a third-party library that is provided with a lib file, failure to do so usually results in the error.

It can also happen that you forget to add the file to the compilation, in which case the object file won't be generated. In gcc you'd add the files to the command line. In MSVS adding the file to the project will make it compile it automatically (albeit files can, manually, be individually excluded from the build).

In Windows programming, the tell-tale sign that you did not link a necessary library is that the name of the unresolved symbol begins with __imp_. Look up the name of the function in the documentation, and it should say which library you need to use. For example, MSDN puts the information in a box at the bottom of each function in a section called "Library".

看春风乍起 2025-01-29 21:20:52

声明但未定义变量或功能。

典型的变量声明是

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 Qualifier:

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)
                          

其他不匹配示例包括

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

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

Declared but did not define a variable or function.

A typical variable declaration is

extern int x;

As this is only a declaration, a single definition is needed. A corresponding definition would be:

int x;

For example, the following would generate an error:

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

Similar remarks apply to functions. Declaring a function without defining it leads to the error:

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

Be careful that the function you implement exactly matches the one you declared. For example, you may have mismatched cv-qualifiers:

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)
                          

Other examples of mismatches include

  • Function/variable declared in one namespace, defined in another.
  • Function/variable declared as class member, defined as global (or vice versa).
  • Function return type, parameter number and types, and calling convention do not all exactly agree.

The error message from the compiler will often give you the full declaration of the variable or function that was declared but never defined. Compare it closely to the definition you provided. Make sure every detail matches.

撩发小公举 2025-01-29 21:20:52

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

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

为了重复重复,顺序很重要!

The order in which interdependent linked libraries are specified is wrong.

The order in which libraries are linked DOES matter if the libraries depend on each other. In general, if library A depends on library B, then libA MUST appear before libB in the linker flags.

For example:

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

Create the libraries:

$ 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

Compile:

$ 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

So to repeat again, the order DOES matter!

永不分离 2025-01-29 21:20:52

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

void foo()在C程序中定义了

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

函数(或变量) AS:

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

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

extern "C" void foo();

您尝试使用它在C ++程序中使用它C ++链接。

如果整个库都包含在标题文件中(并将其编译为C代码);包括的包含如下;

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

Symbols were defined in a C program and used in C++ code.

The function (or variable) void foo() was defined in a C program and you attempt to use it in a C++ program:

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

The C++ linker expects names to be mangled, so you have to declare the function as:

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

Equivalently, instead of being defined in a C program, the function (or variable) void foo() was defined in C++ but with C linkage:

extern "C" void foo();

and you attempt to use it in a C++ program with C++ linkage.

If an entire library is included in a header file (and was compiled as C code); the include will need to be as follows;

extern "C" {
    #include "cheader.h"
}
伤感在游骋 2025-01-29 21:20:52

什么是“未定义的参考/未解决的外部符号”

我会尝试解释什么是“未定义的参考/未解决的外部符号”。

注意:我使用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

并运行IT

$ ./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

OK(没有错误),因为我们只构建对象文件,链接尚未完成。
尝试将其链接到

$ 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没有标签,这就是为什么Linker找不到它的原因。但是我们是黑客:)我们可以修复它。在您的文本编辑器中打开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

IE您应该像下面一样,

    .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 Outption(符号)

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

现在LOCAL_VAR_NAME已绑定Global(as local)

链接

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

并运行它

$ ./prog 
123456789

,我们将其入侵::)

因此,因此,因此,“未定义的参考/未解决的外部符号错误“当链接器无法在对象文件中找到全局符号时,就会发生错误。

what is an "undefined reference/unresolved external symbol"

I'll try to explain what is an "undefined reference/unresolved external symbol".

note: i use g++ and Linux and all examples is for it

For example we have some code

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

and

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

Make object files

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

After the assembler phase we have an object file, which contains any symbols to export.
Look at the symbols

$ 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]

I've rejected some lines from output, because they do not matter

So, we see follow symbols to export.

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

src2.cpp exports nothing and we have seen no its symbols

Link our object files

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

and run it

$ ./prog
123

Linker sees exported symbols and links it. Now we try to uncomment lines in src2.cpp like here

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

and rebuild an object file

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

OK (no errors), because we only build object file, linking is not done yet.
Try to link

$ 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

It has happened because our local_var_name is static, i.e. it is not visible for other modules.
Now more deeply. Get the translation phase output

$ 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

So, we've seen there is no label for local_var_name, that's why linker hasn't found it. But we are hackers :) and we can fix it. Open src1.s in your text editor and change

.local  _ZL14local_var_name
.comm   _ZL14local_var_name,4,4

to

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

i.e. you should have like below

    .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:
; ...

we have changed the visibility of local_var_name and set its value to 456789.
Try to build an object file from it

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

ok, see readelf output (symbols)

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

now local_var_name has Bind GLOBAL (was LOCAL)

link

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

and run it

$ ./prog 
123456789

ok, we hack it :)

So, as a result - an "undefined reference/unresolved external symbol error" happens when the linker cannot find global symbols in the object files.

绮筵 2025-01-29 21:20:52

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

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

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

If all else fails, recompile.

I was recently able to get rid of an unresolved external error in Visual Studio 2012 just by recompiling the offending file. When I re-built, the error went away.

This usually happens when two (or more) libraries have a cyclic dependency. Library A attempts to use symbols in B.lib and library B attempts to use symbols from A.lib. Neither exist to start off with. When you attempt to compile A, the link step will fail because it can't find B.lib. A.lib will be generated, but no dll. You then compile B, which will succeed and generate B.lib. Re-compiling A will now work because B.lib is now found.

趴在窗边数星星i 2025-01-29 21:20:52

模板实现不可见。

非专业化模板必须具有使用它们的所有翻译单元可见的定义。这意味着您不能分开模板的定义
到实现文件。如果您必须将实现分开,则通常的解决方法是拥有一个 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 的定义移至标题文件或使用它的转换单元可见的某个位置。

可以在实现文件中实现专业模板,并且不必可见实现,但是必须先声称专业化。

有关进一步的解释和另一个可能的解决方案(显式实例化),请参见这个问题和答案

Template implementations not visible.

Unspecialized templates must have their definitions visible to all translation units that use them. That means you can't separate the definition of a template
to an implementation file. If you must separate the implementation, the usual workaround is to have an impl file which you include at the end of the header that
declares the template. A common situation is:

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

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

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

To fix this, you must move the definition of X::foo to the header file or some place visible to the translation unit that uses it.

Specialized templates can be implemented in an implementation file and the implementation doesn't have to be visible, but the specialization must be previously declared.

For further explanation and another possible solution (explicit instantiation) see this question and answer.

鹿童谣 2025-01-29 21:20:52

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

a。什么是符号?
简而言之,符号是一个名称。它可以是一个变量名称,函数名称,类名称,typedef名称或除了属于C ++语言的那些名称和标志之外的任何内容。它是由依赖项库(另一个用户定义)定义或介绍的用户。

b。什么是外部?
在VC ++中,每个源文件(.cpp,.c等)被视为翻​​译单元,编译器一次编译一个单元,并为当前翻译单元生成一个对象文件(.OBJ)。 (请注意,包含此源文件的每个标头文件都将进行预处理,并将被视为该翻译单元的一部分)翻译单元中的所有内容都被视为内部,其他所有内容都被视为外部。在C ++中,您可以使用 extern __ extspec(dllimport)等关键字来引用外部符号。

c。什么是“解决”?
解析是一个链接时间。在链接时间时,链接器试图找到对象文件中无法在内部找到其定义的每个符号的外部定义。此搜索过程的范围包括:

  • 在编译时间中生成的所有对象文件,
  • 所有库(.lib)是明确或隐式的
    指定为该建筑物应用程序的其他依赖关系。

此搜索过程称为Resolve。

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

e。 LNK2019的可能原因:未解决的外部符号错误。
我们已经知道,此错误是由于链接器未能找到外部符号的定义,因此可能的原因可以按以下方式排序:

  1. 将其排序。

例如,如果我们具有在A.CPP中定义的称为FOO的函数,则

int foo()
{
    return 0;
}

可以 我们要调用function foo,因此我们添加

void foo();

到声明函数foo(),然后在另一个功能主体中调用它,例如 bar()

void bar()
{
    foo();
}

现在,当您构建此代码时,您将获得LNK2019错误该Foo是未解决的符号。在这种情况下,我们知道FOO()在A.CPP中具有其定义,但与我们所调用的定义不同(不同的返回值)。存在定义的情况。

  1. 则不存在定义

如果我们要在库中调用某些函数, ,但是未将导入库添加到附加依赖项列表中(设置为: project | properties |配置属性|链接|链接|输入|附加依赖关系)您的项目设置。现在,链接器将报告LNK2019,因为该定义在当前搜索范围中不存在。

This is one of most confusing error messages that every VC++ programmers have seen time and time again. Let’s make things clarity first.

A. What is symbol?
In short, a symbol is a name. It can be a variable name, a function name, a class name, a typedef name, or anything except those names and signs that belong to C++ language. It is user defined or introduced by a dependency library (another user-defined).

B. What is external?
In VC++, every source file (.cpp,.c,etc.) is considered as a translation unit, the compiler compiles one unit at a time, and generate one object file(.obj) for the current translation unit. (Note that every header file that this source file included will be preprocessed and will be considered as part of this translation unit)Everything within a translation unit is considered as internal, everything else is considered as external. In C++, you may reference an external symbol by using keywords like extern, __declspec (dllimport) and so on.

C. What is “resolve”?
Resolve is a linking-time term. In linking-time, linker attempts to find the external definition for every symbol in object files that cannot find its definition internally. The scope of this searching process including:

  • All object files that generated in compiling time
  • All libraries (.lib) that are either explicitly or implicitly
    specified as additional dependencies of this building application.

This searching process is called resolve.

D. Finally, why Unresolved External Symbol?
If the linker cannot find the external definition for a symbol that has no definition internally, it reports an Unresolved External Symbol error.

E. Possible causes of LNK2019: Unresolved External Symbol error.
We already know that this error is due to the linker failed to find the definition of external symbols, the possible causes can be sorted as:

  1. Definition exists

For example, if we have a function called foo defined in a.cpp:

int foo()
{
    return 0;
}

In b.cpp we want to call function foo, so we add

void foo();

to declare function foo(), and call it in another function body, say bar():

void bar()
{
    foo();
}

Now when you build this code you will get a LNK2019 error complaining that foo is an unresolved symbol. In this case, we know that foo() has its definition in a.cpp, but different from the one we are calling(different return value). This is the case that definition exists.

  1. Definition does not exist

If we want to call some functions in a library, but the import library is not added into the additional dependency list (set from: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency) of your project setting. Now the linker will report a LNK2019 since the definition does not exist in current searching scope.

や莫失莫忘 2025-01-29 21:20:52

跨模块/DLL(编译器特定)的错误导入/导出方法/类。

MSV要求您使用 __ extspec(dllexport) __ extspec(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
{
};

Incorrectly importing/exporting methods/classes across modules/dll (compiler specific).

MSVS requires you to specify which symbols to export and import using __declspec(dllexport) and __declspec(dllimport).

This dual functionality is usually obtained through the use of a macro:

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

The macro THIS_MODULE would only be defined in the module that exports the function. That way, the declaration:

DLLIMPEXP void foo();

expands to

__declspec(dllexport) void foo();

and tells the compiler to export the function, as the current module contains its definition. When including the declaration in a different module, it would expand to

__declspec(dllimport) void foo();

and tells the compiler that the definition is in one of the libraries you linked against (also see 1)).

You can similary import/export classes:

class DLLIMPEXP X
{
};
日久见人心 2025-01-29 21:20:52

未定义的引用 winmain@16 或类似'不寻常' main()输入点参考(尤其是 Visual-studio )。

您可能已经错过了使用实际IDE选择正确的项目类型。 IDE可能需要将例如Windows Application Projects绑定到此类输入点功能(如上所述所缺少的参考),而不是常用的 int main(int argc,char ** argv); 签名。

如果您的IDE支持普通控制台项目您可能需要选择此项目类型,而不是Windows应用程序项目。


这是 and case2 更详细地从A Real World World World World 问题中处理。

undefined reference to WinMain@16 or similar 'unusual' main() entry point reference (especially for ).

You may have missed to choose the right project type with your actual IDE. The IDE may want to bind e.g. Windows Application projects to such entry point function (as specified in the missing reference above), instead of the commonly used int main(int argc, char** argv); signature.

If your IDE supports Plain Console Projects you might want to choose this project type, instead of a windows application project.


Here are case1 and case2 handled in more detail from a real world problem.

清引 2025-01-29 21:20:52

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

Also if you're using 3rd party libraries make sure you have the correct 32/64 bit binaries

一个人的旅程 2025-01-29 21:20:52

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

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

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

Microsoft offers a #pragma to reference the correct library at link time;

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

In addition to the library path including the directory of the library, this should be the full name of the library.

神经暖 2025-01-29 21:20:52

Visual Studio Nuget软件包需要更新新的Toolset版本

我只是遇到了这个问题,试图将libpng与Visual Studio 2013链接起来。问题是该软件包文件仅具有Visual Studio 2010和2012的库。

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

I edited the package (in the packages folder inside the solution's directory) by finding packagename\build\native\packagename.targets and inside that file, copying all the V110 部分。我将 v110 更改为 v120 in 条件字段仅非常小心地将文件名路径全部留为 v110 。这只是允许Visual Studio 2013链接到2012年的图书馆,在这种情况下,它起作用了。

Visual Studio NuGet package needs to be updated for new toolset version

I just had this problem trying to link libpng with Visual Studio 2013. The problem is that the package file only had libraries for Visual Studio 2010 and 2012.

The correct solution is to hope the developer releases an updated package and then upgrade, but it worked for me by hacking in an extra setting for VS2013, pointing at the VS2012 library files.

I edited the package (in the packages folder inside the solution's directory) by finding packagename\build\native\packagename.targets and inside that file, copying all the v110 sections. I changed the v110 to v120 in the condition fields only being very careful to leave the filename paths all as v110. This simply allowed Visual Studio 2013 to link to the libraries for 2012, and in this case, it worked.

耀眼的星火 2025-01-29 21:20:52

假设您有一个用C ++编写的大项目,该项目具有一千个.cpp文件和一千个.h文件。让我们说该项目还取决于十个静态库。假设我们在Windows上,我们在Visual Studio 20XX中构建了项目。 开始编译整个解决方案时(假设我们在解决方案中只有一个项目)

编译的含义是什么?

  • 当您按CTRL + F7 Visual Studio 强>并开始编译具有扩展名.cpp的每个文件。编译顺序是未定义的。因此
  • ,如果.cpp文件取决于其他.h文件以查找符号
    如果存在一个.cpp文件,则可能在文件.cpp中定义或可能无法定义,
  • 该文件在该文件中找不到一个符号,A 编译器时间错误提出消息符号x无法成为 在每个文件中找到
  • extension .cpp是生成一个对象.O的,并且Visual Studio在名为 projectName.cpp.clean.txt 的文件中写入输出,其中包含所有对象文件必须由链接器处理。

汇编的第二步是由链接器完成的。链接器应合并所有对象文件,并最终构建输出(可能是可执行文件或库)

链接项目

  • 解析所有对象文件的 标题中声明的定义(例如:类别的一种方法的代码,如先前的答案中所述,或事件事件的静态变量的初始化,该静态变量的初始化是在类中的成员)
  • 步骤找到仅在 他也在其他库中搜索了他的对象文件。为项目添加新库配置属性 - &gt; vc ++目录 - &gt; Library Directories ,在这里您指定了搜索库的其他文件夹和配置属性 - &gt; 链接器 - &gt; 输入用于指定库的名称。
    - 如果链接器找不到您写的符号。
    错误lnk2001:未解决的外部符号“ void __cdecl foo(void)”(?foo @@ yaxxz)

观察>观察

  1. 一旦链接器找到一个他没有在其他库中搜索的符号因为它
  2. 链接库的顺序确实很重要
  3. 如果链接器在一个静态库中找到外部符号,他在项目的输出中包括符号。时间崩溃可能发生

如何求解这种错误

编译器时间错误:

  • 确保编写C ++项目句法正确。

链接器时间错误

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

Suppose you have a big project written in c++ which has a thousand of .cpp files and a thousand of .h files.And let's says the project also depends on ten static libraries. Let's says we are on Windows and we build our project in Visual Studio 20xx. When you press Ctrl + F7 Visual Studio to start compiling the whole solution ( suppose we have just one project in the solution )

What's the meaning of compilation ?

  • Visual Studio search into file .vcxproj and start compiling each file which has the extension .cpp. Order of compilation is undefined.So you must not assume that the file main.cpp is compiled first
  • If .cpp files depends on additional .h files in order to find symbols
    that may or may not be defined in the file .cpp
  • If exists one .cpp file in which the compiler could not find one symbol, a compiler time error raises the message Symbol x could not be found
  • For each file with extension .cpp is generated an object file .o and also Visual Studio writes the output in a file named ProjectName.Cpp.Clean.txt which contains all object files that must be processed by the linker.

The Second step of compilation is done by Linker.Linker should merge all the object file and build finally the output ( which may be an executable or a library)

Steps In Linking a project

  • Parse all the object files and find the definition which was only declared in headers ( eg: The code of one method of a class as is mentioned in previous answers, or event the initialization of a static variable which is member inside a class)
  • If one symbol could not be found in object files he also is searched in Additional Libraries.For adding a new library to a project Configuration properties -> VC++ Directories -> Library Directories and here you specified additional folder for searching libraries and Configuration properties -> Linker -> Input for specifying the name of the library.
    -If the Linker could not find the symbol which you write in one .cpp he raises a linker time error which may sound like
    error LNK2001: unresolved external symbol "void __cdecl foo(void)" (?foo@@YAXXZ)

Observation

  1. Once the Linker find one symbol he doesn't search in other libraries for it
  2. The order of linking libraries does matter.
  3. If Linker finds an external symbol in one static library he includes the symbol in the output of the project.However, if the library is shared( dynamic ) he doesn't include the code ( symbols ) in output, but Run-Time crashes may occur

How To Solve this kind of error

Compiler Time Error :

  • Make sure you write your c++ project syntactical correct.

Linker Time Error

  • Define all your symbol which you declare in your header files
  • Use #pragma once for allowing compiler not to include one header if it was already included in the current .cpp which are compiled
  • Make sure that your external library doesn't contain symbols that may enter into conflict with other symbols you defined in your header files
  • When you use the template to make sure you include the definition of each template function in the header file for allowing the compiler to generate appropriate code for any instantiations.
感受沵的脚步 2025-01-29 21:20:52

使用链接器来帮助诊断错误,

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

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

对于GCC和Clang;您通常会在命令行中添加 -v -wl, - derbose -v -wl,-v 。可以在此处找到更多细节;

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

Use the linker to help diagnose the error

Most modern linkers include a verbose option that prints out to varying degrees;

  • Link invocation (command line),
  • Data on what libraries are included in the link stage,
  • The location of the libraries,
  • Search paths used.

For gcc and clang; you would typically add -v -Wl,--verbose or -v -Wl,-v to the command line. More details can be found here;

For MSVC, /VERBOSE (in particular /VERBOSE:LIB) is added to the link command line.

窝囊感情。 2025-01-29 21:20:52

编译器/IDE中的一个错误,

我最近遇到了这个问题,事实证明这是Visual Studio Express 2013 的错误。我必须从项目中删除源文件并重新添加它以克服错误。

如果您认为这可能是编译器/IDE的错误:

  • 清洁项目(有些IDE可以选择此操作,您也可以,您也可以
    通过删除对象文件手动执行此操作)
  • 尝试启动一个新项目,
    从原始代码复制所有源代码。

A bug in the compiler/IDE

I recently had this problem, and it turned out it was a bug in Visual Studio Express 2013. I had to remove a source file from the project and re-add it to overcome the bug.

Steps to try if you believe it could be a bug in compiler/IDE:

  • Clean the project (some IDEs have an option to do this, you can also
    manually do it by deleting the object files)
  • Try start a new project,
    copying all source code from the original one.
从﹋此江山别 2025-01-29 21:20:52

链接.lib文件与.dll

我遇到了同样的问题。说我有项目myproject和testproject。我有效地将LIB文件链接到了testProject。但是,该LIB文件是作为构建Myproject的DLL生成的。另外,我不包含MyProject中所有方法的源代码,而仅包含对DLL的输入点的访问。

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

Linked .lib file is associated to a .dll

I had the same issue. Say i have projects MyProject and TestProject. I had effectively linked the lib file for MyProject to the TestProject. However, this lib file was produced as the DLL for the MyProject was built. Also, I did not contain source code for all methods in the MyProject, but only access to the DLL's entry points.

To solve the issue, i built the MyProject as a LIB, and linked TestProject to this .lib file (i copy paste the generated .lib file into the TestProject folder). I can then build again MyProject as a DLL. It is compiling since the lib to which TestProject is linked does contain code for all methods in classes in MyProject.

め可乐爱微笑 2025-01-29 21:20:52

由于人们在链接错误错误时似乎是针对这个问题的,所以我将在此处添加此问题。

使用GCC 5.2.0的链接器错误的一个可能原因是,默认情况下,选择了一个新的libstdc ++库ABI。

如果您获得了有关不确定的引用到涉及std :: __ cxx11命名空间或标签[abi:cxx11]的符号的链接错误,则可能表明您试图将链接到与不同值的对象文件中链接在一起对于_glibcxx_use_cxx11_abi宏。当链接到使用较旧版本的GCC编辑的第三方库时,通常会发生这种情况。如果第三方库无法与新的ABI进行重建,则您需要将代码与旧的ABI重新编译。

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

Since people seem to be directed to this question when it comes to linker errors I am going to add this here.

One possible reason for linker errors with GCC 5.2.0 is that a new libstdc++ library ABI is now chosen by default.

If you get linker errors about undefined references to symbols that involve types in the std::__cxx11 namespace or the tag [abi:cxx11] then it probably indicates that you are trying to link together object files that were compiled with different values for the _GLIBCXX_USE_CXX11_ABI macro. This commonly happens when linking to a third-party library that was compiled with an older version of GCC. If the third-party library cannot be rebuilt with the new ABI then you will need to recompile your code with the old ABI.

So if you suddenly get linker errors when switching to a GCC after 5.1.0 this would be a thing to check out.

苦笑流年记忆 2025-01-29 21:20:52

您的链接会在参考它们的对象文件之前消耗库,然后

  • 尝试编译并将程序链接到GCC工具链。
  • 您的链接指定所有必要的库和库搜索路径,
  • 如果 libfoo 取决于 libbar ,那么您的链接在 libbar之前正确放置 libfoo
  • 您的链接失败了,未定义的引用 错误。
  • 但是所有未定义的某物 s均在您拥有的标题文件中声明
    #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 and Fail:

$ 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 and Fail:

$ 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

如果您编译相同 :并在一个GO中链接:

$ 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 的引用结束未解决的,链接失败。

其次,示例2 ,使用共享库 libz

共享库不是对象文件或类似物品的内容。它是
更像没有 main 功能的 program
而是公开其定义的其他多个符号,以便其他符号
程序可以在运行时使用它们。

今天,许多Linux发行版将配置其GCC工具链,以便其语言驱动程序( GCC g ++ gfortran 等)
指示系统链接器( ld )以 as-As-As-As-neded 基础链接共享库。
您有其中一个发行版。

这意味着,当链接器在链接序列中找到 -Lz
到共享库(Say)/usr/lib/x86_64-linux-gnu/libz.so ,它想知道是否已添加到尚未定义的程序中的任何引用由 libz 导出的定义

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

  • 在运行时,系统程序加载程序将将 libz 的副本加载到
    随着程序加载程序的副本,运行它。

  • 在运行时,每当您的程序指定的内容
    libz ,该参考使用 libz in in in
    相同的过程。

您的程序只想参考由 libz 导出的定义的一件事,
即功能 zlibversion ,仅在 eg2.c 中仅引用一次。
如果链接器将其引用到您的程序,然后找到定义
libz 导出,引用是已解决的

但是当您尝试链接程序时,例如:

gcc -o eg2 -lz eg2.o

事件的顺序与示例1相同的方式是错误的。
在链接器找到 -lz 时, no 参考任何内容
在程序中:它们都在 eg2.o 中,尚未看到。所以
Linker认为 Libz 没有用。当它到达 eg2.o 时,将其添加到程序中,
然后对 zlibversion 有未定义的引用,链接序列完成了;
该引用尚未解决,链接失败。

最后, pkg-config 示例2的变体具有明显的解释。
壳扩张之后:

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

变为:

gcc -o eg2 -lz eg2.o

这只是示例2。

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

链接:

gcc -o eg2 -lz eg2.o

对您来说很好!

(OR:该链接对您有效,例如Fedora 23,但在Ubuntu 16.04上失败了),

这是因为链接起作用的发行版是其中之一
不将其GCC工具链配置为链接共享库 as-as-needed

回到当天,类似Unix的系统链接静态和共享是正常的
图书馆通过不同的规则。链接序列中的静态库是链接的
在示例1中解释的基于的基础上,但共享库是无条件链接的。

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

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

  • 静态和共享库的不同链接规则可能使人困惑
    到不知道 -lfoo 是否在其链接中是否不知道 -lfoo
    即将解析为/some/where/libfoo.a /some/where/libfoo.so
    并且可能不了解共享和静态库之间的区别
    无论如何。

这种权衡导致了当今的分裂状况。一些发行版
更改了他们共享库的海湾合作委员会链接规则,以便 as-as-neded
原理适用于所有库。一些发行版与旧
方式。

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

如果我只是这样做:

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

肯定必须首先编译 eg1.c ,然后链接结果
带有 libmy_lib.a 的对象文件。所以怎么不知道该对象文件
进行链接时需要吗?

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

运行上面的命令时, gcc 数字显示您需要汇编 +
连锁。因此,在幕后,它会生成一个编译命令,并运行
然后,然后生成一个链接命令,然后运行它,好像 you 已经运行了
两个命令:

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

因此,如果您 do 运行这两个命令,则链接会失败。这
您在失败中注意到的唯一区别是GCC已经生成了
编译 +链接案例中的临时对象文件,因为您没有告诉它
使用 eg1.o 。我们看到:

/tmp/ccQk1tvs.o: In function `main'

而不是:

eg1.o: In function `main':

另请参见

指定了相互依存的链接库的顺序是错误的

只是一种方式
在其中您可以获得需要需要的定义的文件
在链接的后面,与提供定义的文件相比。将图书馆放在
引用它们的对象文件是犯同样错误的另一种方式。

Your linkage consumes libraries before the object files that refer to them

  • You are trying to compile and link your program with the GCC toolchain.
  • Your linkage specifies all of the necessary libraries and library search paths
  • If libfoo depends on libbar, then your linkage correctly puts libfoo before libbar.
  • Your linkage fails with undefined reference to something errors.
  • But all the undefined somethings are declared in the header files you have
    #included and are in fact defined in the libraries that you are linking.

Examples are in C. They could equally well be C++

A minimal example involving a static library you built yourself

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

You build your static library:

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

You compile your program:

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

You try to link it with libmy_lib.a and fail:

$ 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

The same result if you compile and link in one step, like:

$ 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

A minimal example involving a shared system library, the compression library libz

eg2.c

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

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

Compile your program:

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

Try to link your program with libz and fail:

$ 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

Same if you compile and link in one go:

$ 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

And a variation on example 2 involving pkg-config:

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

What are you doing wrong?

In the sequence of object files and libraries you want to link to make your
program, you are placing the libraries before the object files that refer to
them. You need to place the libraries after the object files that refer
to them.

Link example 1 correctly:

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

Success:

$ ./eg1 
Hello World

Link example 2 correctly:

$ gcc -o eg2 eg2.o -lz

Success:

$ ./eg2 
1.2.8

Link the example 2 pkg-config variation correctly:

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

The explanation

Reading is optional from here on.

By default, a linkage command generated by GCC, on your distro,
consumes the files in the linkage from left to right in
commandline sequence. When it finds that a file refers to something
and does not contain a definition for it, to will search for a definition
in files further to the right. If it eventually finds a definition, the
reference is resolved. If any references remain unresolved at the end,
the linkage fails: the linker does not search backwards.

First, example 1, with static library my_lib.a

A static library is an indexed archive of object files. When the linker
finds -lmy_lib in the linkage sequence and figures out that this refers
to the static library ./libmy_lib.a, it wants to know whether your program
needs any of the object files in libmy_lib.a.

There is only object file in libmy_lib.a, namely my_lib.o, and there's only one thing defined
in my_lib.o, namely the function hw.

The linker will decide that your program needs my_lib.o if and only if it already knows that
your program refers to hw, in one or more of the object files it has already
added to the program, and that none of the object files it has already added
contains a definition for hw.

If that is true, then the linker will extract a copy of my_lib.o from the library and
add it to your program. Then, your program contains a definition for hw, so
its references to hw are resolved.

When you try to link the program like:

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

the linker has not added eg1.o to the program when it sees
-lmy_lib. Because at that point, it has not seen eg1.o.
Your program does not yet make any references to hw: it
does not yet make any references at all, because all the references it makes
are in eg1.o.

So the linker does not add my_lib.o to the program and has no further
use for libmy_lib.a.

Next, it finds eg1.o, and adds it to be program. An object file in the
linkage sequence is always added to the program. Now, the program makes
a reference to hw, and does not contain a definition of hw; but
there is nothing left in the linkage sequence that could provide the missing
definition. The reference to hw ends up unresolved, and the linkage fails.

Second, example 2, with shared library libz

A shared library isn't an archive of object files or anything like it. It's
much more like a program that doesn't have a main function and
instead exposes multiple other symbols that it defines, so that other
programs can use them at runtime.

Many Linux distros today configure their GCC toolchain so that its language drivers (gcc,g++,gfortran etc)
instruct the system linker (ld) to link shared libraries on an as-needed basis.
You have got one of those distros.

This means that when the linker finds -lz in the linkage sequence, and figures out that this refers
to the shared library (say) /usr/lib/x86_64-linux-gnu/libz.so, it wants to know whether any references that it has added to your program that aren't yet defined have definitions that are exported by libz

If that is true, then the linker will not copy any chunks out of libz and
add them to your program; instead, it will just doctor the code of your program
so that:-

  • At runtime, the system program loader will load a copy of libz into the
    same process as your program whenever it loads a copy of your program, to run it.

  • At runtime, whenever your program refers to something that is defined in
    libz, that reference uses the definition exported by the copy of libz in
    the same process.

Your program wants to refer to just one thing that has a definition exported by libz,
namely the function zlibVersion, which is referred to just once, in eg2.c.
If the linker adds that reference to your program, and then finds the definition
exported by libz, the reference is resolved

But when you try to link the program like:

gcc -o eg2 -lz eg2.o

the order of events is wrong in just the same way as with example 1.
At the point when the linker finds -lz, there are no references to anything
in the program: they are all in eg2.o, which has not yet been seen. So the
linker decides it has no use for libz. When it reaches eg2.o, adds it to the program,
and then has undefined reference to zlibVersion, the linkage sequence is finished;
that reference is unresolved, and the linkage fails.

Lastly, the pkg-config variation of example 2 has a now obvious explanation.
After shell-expansion:

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

becomes:

gcc -o eg2 -lz eg2.o

which is just example 2 again.

I can reproduce the problem in example 1, but not in example 2

The linkage:

gcc -o eg2 -lz eg2.o

works just fine for you!

(Or: That linkage worked fine for you on, say, Fedora 23, but fails on Ubuntu 16.04)

That's because the distro on which the linkage works is one of the ones that
does not configure its GCC toolchain to link shared libraries as-needed.

Back in the day, it was normal for unix-like systems to link static and shared
libraries by different rules. Static libraries in a linkage sequence were linked
on the as-needed basis explained in example 1, but shared libraries were linked unconditionally.

This behaviour is economical at linktime because the linker doesn't have to ponder
whether a shared library is needed by the program: if it's a shared library,
link it. And most libraries in most linkages are shared libraries. But there are disadvantages too:-

  • It is uneconomical at runtime, because it can cause shared libraries to be
    loaded along with a program even if doesn't need them.

  • The different linkage rules for static and shared libraries can be confusing
    to inexpert programmers, who may not know whether -lfoo in their linkage
    is going to resolve to /some/where/libfoo.a or to /some/where/libfoo.so,
    and might not understand the difference between shared and static libraries
    anyway.

This trade-off has led to the schismatic situation today. Some distros have
changed their GCC linkage rules for shared libraries so that the as-needed
principle applies for all libraries. Some distros have stuck with the old
way.

Why do I still get this problem even if I compile-and-link at the same time?

If I just do:

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

surely gcc has to compile eg1.c first, and then link the resulting
object file with libmy_lib.a. So how can it not know that object file
is needed when it's doing the linking?

Because compiling and linking with a single command does not change the
order of the linkage sequence.

When you run the command above, gcc figures out that you want compilation +
linkage. So behind the scenes, it generates a compilation command, and runs
it, then generates a linkage command, and runs it, as if you had run the
two commands:

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

So the linkage fails just as it does if you do run those two commands. The
only difference you notice in the failure is that gcc has generated a
temporary object file in the compile + link case, because you're not telling it
to use eg1.o. We see:

/tmp/ccQk1tvs.o: In function `main'

instead of:

eg1.o: In function `main':

See also

The order in which interdependent linked libraries are specified is wrong

Putting interdependent libraries in the wrong order is just one way
in which you can get files that need definitions of things coming
later in the linkage than the files that provide the definitions. Putting libraries before the
object files that refer to them is another way of making the same mistake.

贱人配狗天长地久 2025-01-29 21:20:52

围绕GNU LD的包装器不支持Linker脚本

某些.So文件实际上是 gnu ld链接脚本,eg

INPUT (libtbb.so.2)

一些更复杂的构建可能不支持这一点。例如,如果您在编译器选项中包含-v,则可以看到 mainwin gcc Wrapper mwdip 在要链接的库库列表中丢弃链接器脚本命令文件。围绕的简单工作是用文件的副本(或symlink)替换链接器脚本输入命令文件,例如

cp libtbb.so.2 libtbb.so

,例如将-l参数替换为.so的完整路径,例如 -ltbb do /home/foo/tbb-4.3/linux/lib/lib/intel64/gcc4.4/libtbb .so.2

A wrapper around GNU ld that doesn't support linker scripts

Some .so files are actually GNU ld linker scripts, e.g. libtbb.so file is an ASCII text file with this contents:

INPUT (libtbb.so.2)

Some more complex builds may not support this. For example, if you include -v in the compiler options, you can see that the mainwin gcc wrapper mwdip discards linker script command files in the verbose output list of libraries to link in. A simple work around is to replace the linker script input command file with a copy of the file instead (or a symlink), e.g.

cp libtbb.so.2 libtbb.so

Or you could replace the -l argument with the full path of the .so, e.g. instead of -ltbb do /home/foo/tbb-4.3/linux/lib/intel64/gcc4.4/libtbb.so.2

硪扪都還晓 2025-01-29 21:20:52

与模板成为朋友...

给定模板类型的代码片段,带有朋友操作员(或函数);

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

运算符&lt; 被声明为非模板功能。对于 t foo 使用的每种类型,都需要有一个非策略的操作员&lt;&lt; 。例如,如果已声明类型 foo&lt; 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 的相应实例化,即运营商&lt;&lt; &lt; int 实例化仅限于访问 foo&lt; int&gt; 实例化的私人成员。

替代方案包括;

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

     模板&lt; typename t&gt;
    类foo {
        模板&lt; typename t1&gt;
        朋友STD :: Ostream&amp;操作员&lt;&lt;(std :: ostream&amp; os,const foo&lt; t1&gt;&amp; a);
        // ...
    };
     
  • or,可以在类别的类别内部进行内联操作员的实现;

     模板&lt; typename t&gt;
    类foo {
        朋友STD :: Ostream&amp;操作员&lt;&lt;(std :: ostream&amp; os,const foo&amp; a)
        {/*....*/}
        // ...
    };
     

note ,当仅在类中出现运算符(或函数)的声明时,该名称不适用于“正常”查找,仅用于参数依赖查找,来自 cppReference ;

在类或类模板中在朋友声明中声明的名称x成为X的最内在命名空间的成员,但在查找中无法访问(除非考虑X的参数依赖性查找),除非在此匹配声明提供名称空间范围...

cppReference C ++ FAQ

代码列表,显示上述技术


作为失败代码样本的旁注; G ++警告此信息如下

警告:朋友声明'std :: ostream&amp;运算符&lt;&lt;(...)'声明非网板函数[-wnon-template-fird]

注意:

Befriending templates...

Given the code snippet of a template type with a friend operator (or function);

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

The operator<< is being declared as a non-template function. For every type T used with Foo, there needs to be a non-templated operator<<. For example, if there is a type Foo<int> declared, then there must be an operator implementation as follows;

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

Since it is not implemented, the linker fails to find it and results in the error.

To correct this, you can declare a template operator before the Foo type and then declare as a friend, the appropriate instantiation. The syntax is a little awkward, but is looks as follows;

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

The above code limits the friendship of the operator to the corresponding instantiation of Foo, i.e. the operator<< <int> instantiation is limited to access the private members of the instantiation of Foo<int>.

Alternatives include;

  • Allowing the friendship to extend to all instantiations of the templates, as follows;

    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
    
  • Or, the implementation for the operator<< can be done inline inside the class definition;

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

Note, when the declaration of the operator (or function) only appears in the class, the name is not available for "normal" lookup, only for argument dependent lookup, from cppreference;

A name first declared in a friend declaration within class or class template X becomes a member of the innermost enclosing namespace of X, but is not accessible for lookup (except argument-dependent lookup that considers X) unless a matching declaration at the namespace scope is provided...

There is further reading on template friends at cppreference and the C++ FAQ.

Code listing showing the techniques above.


As a side note to the failing code sample; g++ warns about this as follows

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)

此刻的回忆 2025-01-29 21:20:52

当您的包含路径是不同的

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

链接器如何工作?通过比较其签名,链接器将函数声明(在标题中声明)与其定义(在共享库中)匹配。如果链接器找不到完美匹配的函数定义,则可以获得链接器错误。

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

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

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

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

您可能会问一个这样的情况如何结束? 当然包括路径!如果在编译共享库时,Inclubel路径会导致 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(...);
}

繁荣!您会收到链接器错误,并且不知道为什么会失败。原因是通用库使用相同的不同版本包括 common_math.h (我在示例中通过包括不同的路径而在此使其显而易见,但并不总是那么明显。也许也许。编译器设置中的包含路径不同。

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

请调试Linker

如果您正在使用Visual Studio, Dumpbin是您的朋友。我敢肯定,其他编译器还有其他类似的工具。

该过程像这样:

  1. 请注意链接器错误中给出的怪异的杂乱无章的名称。 (例如draw@graphics@xyz)。
  2. 将导出的符号从库将导出的符号转移到文本文件中。
  3. 搜索出口的感兴趣符号,并请注意,该名称是不同的。
  4. 请注意为什么操纵名称最终会有所不同。您将能够看到参数类型是不同的,即使它们在源代码中看起来相同。
  5. 原因不同的原因。在上面给出的示例中,由于包括不同的文件,它们是不同的。

[1]通过项目,是指一组源文件,这些文件链接在一起以生成库或可执行文件。

编辑1:重写第一部分以易于理解。请在下面发表评论,让我知道是否需要修复其他问题。谢谢!

When your include paths are different

Linker errors can happen when a header file and its associated shared library (.lib file) go out of sync. Let me explain.

How do linkers work? The linker matches a function declaration (declared in the header) with its definition (in the shared library) by comparing their signatures. You can get a linker error if the linker doesn't find a function definition that matches perfectly.

Is it possible to still get a linker error even though the declaration and the definition seem to match? Yes! They might look the same in source code, but it really depends on what the compiler sees. Essentially you could end up with a situation like this:

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

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

Note how even though both the function declarations look identical in source code, but they are really different according to the compiler.

You might ask how one ends up in a situation like that? Include paths of course! If when compiling the shared library, the include path leads to header1.h and you end up using header2.h in your own program, you'll be left scratching your header wondering what happened (pun intended).

An example of how this can happen in the real world is explained below.

Further elaboration with an example

I have two projects: graphics.lib and main.exe. Both projects depend on common_math.h. Suppose the library exports the following function:

// graphics.lib    
#include "common_math.h" 
   
void draw(vec3 p) { ... } // vec3 comes from common_math.h

And then you go ahead and include the library in your own project.

// main.exe
#include "other/common_math.h"
#include "graphics.h"

int main() {
    draw(...);
}

Boom! You get a linker error and you have no idea why it's failing. The reason is that the common library uses different versions of the same include common_math.h (I have made it obvious here in the example by including a different path, but it might not always be so obvious. Maybe the include path is different in the compiler settings).

Note in this example, the linker would tell you it couldn't find draw(), when in reality you know it obviously is being exported by the library. You could spend hours scratching your head wondering what went wrong. The thing is, the linker sees a different signature because the parameter types are slightly different. In the example, vec3 is a different type in both projects as far as the compiler is concerned. This could happen because they come from two slightly different include files (maybe the include files come from two different versions of the library).

Debugging the linker

DUMPBIN is your friend, if you are using Visual Studio. I'm sure other compilers have other similar tools.

The process goes like this:

  1. Note the weird mangled name given in the linker error. (eg. draw@graphics@XYZ).
  2. Dump the exported symbols from the library into a text file.
  3. Search for the exported symbol of interest, and notice that the mangled name is different.
  4. Pay attention to why the mangled names ended up different. You would be able to see that the parameter types are different, even though they look the same in the source code.
  5. Reason why they are different. In the example given above, they are different because of different include files.

[1] By project I mean a set of source files that are linked together to produce either a library or an executable.

EDIT 1: Rewrote first section to be easier to understand. Please comment below to let me know if something else needs to be fixed. Thanks!

十年不长 2025-01-29 21:20:52

不一致的 unicode 定义

Windows Unicode构建是使用 tchar 等构建的。被定义为 WCHAR_T 等。代码>用 TCHAR 定义为构建为 char 等。这些 unicode _unicode 定义定义会影响所有“ noreferrer”>“字符串类型; lptstr lpctstr 及其麋鹿。

使用 Unicode 定义并尝试将其链接在 Unicode 未定义的项目中,将导致链接器错误,因为在 unicode > tchar ; char vs. wchar_t

错误通常包括一个函数一个值 char wchar_t 派生类型,这些函数可以包括 std :: Basic__string&lt;&gt; 等。出色地。当浏览代码中的受影响函数时,通常会引用 tchar std :: basic_string&lt; tchar&gt; 等。该代码最初是针对Unicode和多字节字符(或“狭窄”)构建的。

要纠正这一点,请构建所有必需的库和项目,并具有一致的 Unicode (和 _unicode )的一致定义。

  1. 这可以用;

    完成

      #define Unicode
    #define _unicode
     
  2. 或项目设置;

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

  3. 或命令行;

      /dunicode /d_unicode
     

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

不要忘记在“发行”和“调试”构建之间保持一致。

Inconsistent UNICODE definitions

A Windows UNICODE build is built with TCHAR etc. being defined as wchar_t etc. When not building with UNICODE defined as build with TCHAR defined as char etc. These UNICODE and _UNICODE defines affect all the "T" string types; LPTSTR, LPCTSTR and their elk.

Building one library with UNICODE defined and attempting to link it in a project where UNICODE is not defined will result in linker errors since there will be a mismatch in the definition of TCHAR; char vs. wchar_t.

The error usually includes a function a value with a char or wchar_t derived type, these could include std::basic_string<> etc. as well. When browsing through the affected function in the code, there will often be a reference to TCHAR or std::basic_string<TCHAR> etc. This is a tell-tale sign that the code was originally intended for both a UNICODE and a Multi-Byte Character (or "narrow") build.

To correct this, build all the required libraries and projects with a consistent definition of UNICODE (and _UNICODE).

  1. This can be done with either;

    #define UNICODE
    #define _UNICODE
    
  2. Or in the project settings;

    Project Properties > General > Project Defaults > Character Set

  3. Or on the command line;

    /DUNICODE /D_UNICODE
    

The alternative is applicable as well, if UNICODE is not intended to be used, make sure the defines are not set, and/or the multi-character setting is used in the projects and consistently applied.

Do not forget to be consistent between the "Release" and "Debug" builds as well.

宁愿没拥抱 2025-01-29 21:20:52

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

来自C的人,这可能是一个惊喜的是,在C ++ Global const 变量中具有内部(或静态)连锁。在C中,情况并非如此,因为所有全局变量均隐含 extern (即当缺少 static> 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中声明使用explicit 外部

Missing "extern" in const variable declarations/definitions (C++ only)

For people coming from C it might be a surprise that in C++ global constvariables have internal (or static) linkage. In C this was not the case, as all global variables are implicitly extern (i.e. when the static keyword is missing).

Example:

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

correct would be to use a header file and include it in file2.cpp and file1.cpp

extern const int test;
extern int test2;

Alternatively one could declare the const variable in file1.cpp with explicit extern

放血 2025-01-29 21:20:52

清洁和重建

构建的“清洁”可以消除可能从以前的构建中剩下的“死木”,失败的构建,不完整的构建和其他构建系统相关的构建问题。

通常,IDE或构建将包含某种形式的“清洁”功能,但这可能无法正确配置(例如,在手动制作中)或可能失败(例如,中间或结果二进制文件是仅读取的)。

“清洁”完成后,请验证“清洁”成功并成功删除了所有生成的中间文件(例如自动化的makefile)。

这个过程可以看作是最终度假胜地,但通常是一个很好的第一步。特别是如果最近添加了与错误相关的代码(本地或源存储库中)。

Clean and rebuild

A "clean" of the build can remove the "dead wood" that may be left lying around from previous builds, failed builds, incomplete builds and other build system related build issues.

In general the IDE or build will include some form of "clean" function, but this may not be correctly configured (e.g. in a manual makefile) or may fail (e.g. the intermediate or resultant binaries are read-only).

Once the "clean" has completed, verify that the "clean" has succeeded and all the generated intermediate file (e.g. an automated makefile) have been successfully removed.

This process can be seen as a final resort, but is often a good first step; especially if the code related to the error has recently been added (either locally or from the source repository).

青芜 2025-01-29 21:20:52

即使这是一个非常古老的问题,有多个接受的答案,我还是想分享如何解决晦涩的“对“错误”错误。

我正在使用别名的不同版本

来参考 std :: filesystem :: path :文件系统自C ++ 17以来位于标准库中++ 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&lt; <强>实验::文件系统&gt;并包含上面的代码
  • file.cpp ,file.h,#include的“ file.h
  • main.cpp # ; 文件系统&gt;和“ 文件

。由于main.cpp#include'd“ file.h ”之后。我用以下命令来编译程序:

$ g ++ -g -std = C ++ 17 -C main.cpp - &gt;将main.cpp汇编为main.o
$ g ++ -g -std = c ++ 17 -c file.cpp - &gt; compiles file.cpp和file.h to file.o
美元links main.o and file.o

以下方式 file.o中包含的任何函数在main。参考“错误,因为 main.o 转介 std :: fileSystem :: path file.o to < strong> std ::实验:: filesystem :: path

解决此问题的解决方案

我只需要更改&lt;实验:: filesystem&gt;在文件中。

Even though this is a pretty old questions with multiple accepted answers, I'd like to share how to resolve an obscure "undefined reference to" error.

Different versions of libraries

I was using an alias to refer to std::filesystem::path: filesystem is in the standard library since C++17 but my program needed to also compile in C++14 so I decided to use a variable alias:

#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

Let's say I have three files: main.cpp, file.h, file.cpp:

  • file.h #include's <experimental::filesystem> and contains the code above
  • file.cpp, the implementation of file.h, #include's "file.h"
  • main.cpp #include's <filesystem> and "file.h"

Note the different libraries used in main.cpp and file.h. Since main.cpp #include'd "file.h" after <filesystem>, the version of filesystem used there was the C++17 one. I used to compile the program with the following commands:

$ g++ -g -std=c++17 -c main.cpp -> compiles main.cpp to main.o
$ g++ -g -std=c++17 -c file.cpp -> compiles file.cpp and file.h to file.o
$ g++ -g -std=c++17 -o executable main.o file.o -lstdc++fs -> links main.o and file.o

This way any function contained in file.o and used in main.o that required path_t gave "undefined reference" errors because main.o referred to std::filesystem::path but file.o to std::experimental::filesystem::path.

Resolution

To fix this I just needed to change <experimental::filesystem> in file.h to <filesystem>.

浪推晚风 2025-01-29 21:20:52

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

GCC的默认行为是所有符号都是可见的。但是,当使用选项 -fvisibility = hidden 构建翻译单元时,只有用 __属性标记的函数/符号__((vistibility(vistibility(“ 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

您还可以在选项 -c 中使用 nm 来删除名称(如果使用C ++)。

与Windows-dll类似,一个人会用定义标记公共功能,例如 dll_public 定义为:

#define DLL_PUBLIC __attribute__ ((visibility ("default")))

DLL_PUBLIC int my_public_function(){
  ...
}

大致对应于Windows'/msvc-version:

#ifdef BUILDING_DLL
    #define DLL_PUBLIC __declspec(dllexport) 
#else
    #define DLL_PUBLIC __declspec(dllimport) 
#endif

更多


当翻译单元用 -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

When linking against shared libraries, make sure that the used symbols are not hidden.

The default behavior of gcc is that all symbols are visible. However, when the translation units are built with option -fvisibility=hidden, only functions/symbols marked with __attribute__ ((visibility ("default"))) are external in the resulting shared object.

You can check whether the symbols your are looking for are external by invoking:

# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL 

the hidden/local symbols are shown by nm with lowercase symbol type, for example t instead of `T for code-section:

nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL

You can also use nm with the option -C to demangle the names (if C++ was used).

Similar to Windows-dlls, one would mark public functions with a define, for example DLL_PUBLIC defined as:

#define DLL_PUBLIC __attribute__ ((visibility ("default")))

DLL_PUBLIC int my_public_function(){
  ...
}

Which roughly corresponds to Windows'/MSVC-version:

#ifdef BUILDING_DLL
    #define DLL_PUBLIC __declspec(dllexport) 
#else
    #define DLL_PUBLIC __declspec(dllimport) 
#endif

More information about visibility can be found on the gcc wiki.


When a translation unit is compiled with -fvisibility=hidden the resulting symbols have still external linkage (shown with upper case symbol type by nm) and can be used for external linkage without problem if the object files become part of a static libraries. The linkage becomes local only when the object files are linked into a shared library.

To find which symbols in an object file are hidden run:

>>> objdump -t XXXX.o | grep hidden
0000000000000000 g     F .text  000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g     F .text  000000000000000b .hidden HIDDEN_SYMBOL2
愁杀 2025-01-29 21:20:52

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

因此,当链接器找不到链接对象的定义时,它会发出“未定义的参考”错误。从定义来看,此错误发生在链接过程的后期阶段。有多种原因导致“未定义的参考”错误。

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

#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 ++ ID中指定错误的项目类型并尝试做项目不期望的事情时,我们会获得“未定义的参考”。

#5)没有库

如果程序员未正确指定图书馆路径或完全忘记了指定它,那么我们将获得程序从库中使用的所有参考文献。

#6)未编译依赖性文件

程序员必须确保我们事先编译项目的所有依赖项,以便当我们编译项目时,编译器会找到所有依赖项并成功地编译。如果任何依赖项都缺少,则编译器给出“未定义的参考”。

除了上面讨论的原因外,在许多其他情况下可能会出现“未定义的参考”错误。但最重要的是,程序员弄错了问题,为了防止此错误,应纠正它们。

An “Undefined Reference” error occurs when we have a reference to object name (class, function, variable, etc.) in our program and the linker cannot find its definition when it tries to search for it in all the linked object files and libraries.

Thus when the linker cannot find the definition of a linked object, it issues an “undefined reference” error. As clear from definition, this error occurs in the later stages of the linking process. There are various reasons that cause an “undefined reference” error.

Some possible reason(more frequent):

#1) No Definition Provided For Object

This is the simplest reason for causing an “undefined reference” error. The programmer has simply forgotten to define the object.

Consider the following C++ program. Here we have only specified the prototype of function and then used it in the main function.

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

Output:

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

So when we compile this program, the linker error that says “undefined reference to ‘func1()’” is issued.

In order to get rid of this error, we correct the program as follows by providing the definition of the function func1. Now the program gives the appropriate output.

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

Output:

hello, world!!

#2) Wrong Definition (signatures don’t match) Of Objects Used

Yet another cause for “undefined reference” error is when we specify wrong definitions. We use any object in our program and its definition is something different.

Consider the following C++ program. Here we have made a call to func1 (). Its prototype is int func1 (). But its definition does not match with its prototype. As we see, the definition of the function contains a parameter to the function.

Thus when the program is compiled, the compilation is successful because of the prototype and function call match. But when the linker is trying to link the function call with its definition, it finds the problem and issues the error as “undefined reference”.

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

Output:

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

Thus to prevent such errors, we simply cross-check if the definitions and usage of all the objects are matching in our program.

#3) Object Files Not Linked Properly

This issue can also give rise to the “undefined reference” error. Here, we may have more than one source files and we might compile them independently. When this is done, the objects are not linked properly and it results in “undefined reference”.

Consider the following two C++ programs. In the first file, we make use of the “print ()” function which is defined in the second file. When we compile these files separately, the first file gives “undefined reference” for the print function, while the second file gives “undefined reference” for the main function.

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

Output:

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

int print() {
    return 42;
}

Output:

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

The way to resolve this error is to compile both the files simultaneously (For example, by using g++).

Apart from the causes already discussed, “undefined reference” may also occur because of the following reasons.

#4) Wrong Project Type

When we specify wrong project types in C++ IDEs like the visual studio and try to do things that the project does not expect, then, we get “undefined reference”.

#5) No Library

If a programmer has not specified the library path properly or completely forgotten to specify it, then we get an “undefined reference” for all the references the program uses from the library.

#6) Dependent Files Are Not Compiled

A programmer has to ensure that we compile all the dependencies of the project beforehand so that when we compile the project, the compiler finds all the dependencies and compiles successfully. If any of the dependencies are missing then the compiler gives “undefined reference”.

Apart from the causes discussed above, the “undefined reference” error can occur in many other situations. But the bottom line is that the programmer has got the things wrong and in order to prevent this error they should be corrected.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文