C++ 什么是未定义的引用/未解析的外部符号错误以及如何修复它?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12573816/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 16:23:17  来源:igfitidea点击:

What is an undefined reference/unresolved external symbol error and how do I fix it?

c++linker-errorsundefined-referencec++-faqunresolved-external

提问by Luchian Grigore

What are undefined reference/unresolved external symbol errors? What are common causes and how to fix/prevent them?

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

Feel free to edit/add your own.

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

采纳答案by Luchian Grigore

Compiling a C++ program takes place in several steps, as specified by 2.2(credits to Keith Thompson for the reference):

编译 C++ 程序分几个步骤进行,如2.2所指定(感谢 Keith Thompson 提供参考)

The precedence among the syntax rules of translation is specified by the following phases [see footnote].

  1. Physical source file characters are mapped, in an implementation-defined manner, to the basic source character set (introducing new-line characters for end-of-line indicators) if necessary. [SNIP]
  2. Each instance of a backslash character (\) immediately followed by a new-line character is deleted, splicing physical source lines to form logical source lines. [SNIP]
  3. The source file is decomposed into preprocessing tokens (2.5) and sequences of white-space characters (including comments). [SNIP]
  4. Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. [SNIP]
  5. Each source character set member in a character literal or a string literal, as well as each escape sequence and universal-character-name in a character literal or a non-raw string literal, is converted to the corresponding member of the execution character set; [SNIP]
  6. Adjacent string literal tokens are concatenated.
  7. White-space characters separating tokens are no longer significant. Each preprocessing token is converted into a token. (2.7). The resulting tokens are syntactically and semantically analyzed and translated as a translation unit. [SNIP]
  8. Translated translation units and instantiation units are combined as follows: [SNIP]
  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.(emphasis mine)

[footnote]Implementations must behave as if these separate phases occur, although in practice different phases might be folded together.

翻译的语法规则之间的优先级由以下阶段指定[见脚注]

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

[footnote]实现必须表现得好像这些单独的阶段发生一样,尽管在实践中不同的阶段可能会折叠在一起。

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 implementation files into object files or libraries and now you want to get them to work together.

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

Say you defined symbol ain a.cpp. Now, b.cppdeclaredthat symbol and used it. Before linking, it simply assumes that that symbol was defined somewhere, but it doesn't yet care where. The linking phase is responsible for finding the symbol and correctly linking it to b.cpp(well, actually to the object or library that uses it).

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

If you're using Microsoft Visual Studio, you'll see that projects generate .libfiles. 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).

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

Similar mechanisms exist for other compilers/ platforms.

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

Common error messages are error LNK2001, error LNK1120, error LNK2019for Microsoft Visual Studioand undefined reference tosymbolNamefor GCC.

常见的错误信息error LNK2001error LNK1120error LNK2019对于微软的Visual Studioundefined reference to符号名称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:

使用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:

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

常见原因包括:

回答by Luchian Grigore

Class members:

班级成员:

A pure virtualdestructor needs an implementation.

virtual析构函数需要一个实现。

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.

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

virtualmethods must either be implemented or defined as pure.

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

This is similar to non-virtualmethods 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:

这类似于virtual没有定义的非方法,附加的推理是纯声明生成一个虚拟 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
}

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

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

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

Non-virtualclass members

virtual班级成员

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 inlineto prevent a multiple definition.

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

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

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

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

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 staticconstdata 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 constdata members.

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

回答by Luchian Grigore

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 gccyou would specify all object files that are to be linked together in the command line, or compile the implementation files together.

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

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

The libraryNamehere is just the bare name of the library, without platform-specific additions. So e.g. on Linux library files are usually called libfoo.sobut 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 -lor -L.

libraryName这里只是图书馆的裸名,不特定于平台的补充。因此,例如在 Linux 上通常会调用库文件,libfoo.so但您只会编写-lfoo. 在 Windows 上,可能会调用相同的文件foo.lib,但您将使用相同的参数。您可能必须添加可以使用-L?directory?. 确保不要在-l或之后写空格-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.

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

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

MSVS 下,添加到项目中的lib文件会自动将它们的目标文件链接在一起并生成一个文件(通常使用)。要在单独的项目中使用这些符号,您需要lib在项目设置中包含这些文件。这是在项目属性的链接器部分完成的,在Input -> Additional Dependencies. (lib文件路径要加在Linker -> General -> Additional Library Directories) 使用lib文件自带的第三方库时,如果不这样做,通常会导致错误。

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 gccyou'd add the files to the command line. In MSVSadding the file to the project will make it compile it automatically (albeit files can, manually, be individually excluded from the build).

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

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".

在 Windows 编程中,未链接必要库的明显标志是未解析符号的名称以__imp_. 在文档中查找函数的名称,它应该说明您需要使用哪个库。例如,MSDN 将信息放在每个函数底部的一个框中,称为“库”部分。

回答by Luchian Grigore

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

请注意您实现的函数与您声明的函数完全匹配。例如,您可能有不匹配的 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)

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.

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

回答by Svalorzen

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 Adepends on library B, then libAMUSTappear before libBin the linker flags.

如果库相互依赖,链接库的顺序很重要。通常,如果 libraryA依赖于 library B,则libA必须出现libB在链接器标志之前。

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 DOESmatter!

所以,再次重复,顺序DOES事!

回答by Kastaneda

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

注意:我使用 g++ 和 Linux,所有示例都是针对它的

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

src2.cpp 什么也没有导出,我们也没有看到它的符号

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 中的行的注释

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

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

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

这是因为我们的 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

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_var_name 没有标签,这就是链接器没有找到它的原因。但我们是黑客 :) 我们可以修复它。在文本编辑器中打开 src1.s 并更改

.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

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

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

ok, see readelf output (symbols)

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

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

now local_var_name has Bind GLOBAL (was LOCAL)

现在 local_var_name 具有 Bind GLOBAL (原为 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.

因此,当链接器在目标文件中找不到全局符号时,就会发生“未定义的引用/未解析的外部符号错误”。

回答by Luchian Grigore

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

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

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

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

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

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

C++ 链接器期望名称被修改,因此您必须将函数声明为:

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:

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

extern "C" void foo();

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

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

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

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

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

回答by sgryzko

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.

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

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.

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

回答by Luchian Grigore

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

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

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

MSVS 要求您使用__declspec(dllexport)和指定要导出和导入的符号__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_MODULEwould only be defined in the module that exports the function. That way, the declaration:

THIS_MODULE将仅在导出函数的模块中定义。这样,声明:

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)).

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

You can similary import/export classes:

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

class DLLIMPEXP X
{
};

回答by Nima Soroush

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

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

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).

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

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.

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

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:

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

  • 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.
  • 编译时生成的所有目标文件
  • 显式或隐式指定为此构建应用程序的附加依赖项的所有库 (.lib)。

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.

D. 最后,为什么是未解析的外部符号?如果链接器找不到内部没有定义的符号的外部定义,则会报告未解析的外部符号错误。

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:

E. LNK2019 的可能原因:Unresolved External Symbol error。我们已经知道这个错误是由于链接器没有找到外部符号的定义,可能的原因可以归结为:

  1. Definition exists
  1. 定义存在

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

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

int foo()
{
    return 0;
}

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

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

void foo();

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

声明函数 foo(),并在另一个函数体中调用它,例如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.

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

  1. Definition does not exist
  1. 定义不存在

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.

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