链接器工具错误 LNK2019

函数“function”中引用了未解析的外部 (external) 符号“symbol”

function 的编译代码引用或调用 symbol,但链接器在所有库或目标文件中都不到符号定义

此错误消息后为错误 LNK1120。 若要修复错误 LNK1120,必须先修复所有 LNK2001 和 LNK2019 错误。

可能的原因

有多种方法会造成此错误。 所有这些方法都涉及引用链接器无法解析或无法查找其定义的函数或变量。 编译器可以识别未声明符号的情况,但无法判断未定义符号的情况。 这是因为定义可能位于不同的源文件或库中。 如果引用了未定义的符号,链接器会生成未解析的外部 (external) 符号错误。

以下是一些导致 LNK2019 的常见问题:

包含符号定义的源文件未编译

在 Visual Studio 中,确保将定义符号的源文件编译为项目的一部分。 检查中间生成输出目录中是否有匹配的 .obj 文件。 如果源文件未编译,请右键单击“解决方案资源管理器”中的文件,然后选择“属性”以检查文件的属性。 “配置属性”>“常规”页应显示 C/C++ 编译器的项类型。 在命令行上,确保编译了包含定义的源文件。

包含符号定义的目标文件或库未链接

在 Visual Studio 中,确保包含符号定义的对象文件或库作为项目的一部分链接。 在命令行上,确保要链接的文件列表包含对象文件或库。

符号声明与符号定义的拼写不一样

验证在声明和定义中以及在使用或调用符号的任何地方使用了正确的拼写和大写。

使用了函数,但是参数的类型或数量与函数定义不匹配

函数声明必须匹配定义。 确保函数调用与声明匹配,并且声明与定义匹配。 调用函数模板的代码还必须拥有包括与定义相同的模板参数的匹配函数模板声明。 有关模板声明不匹配的示例,请参阅“示例”部分中的示例 LNK2019e.cpp。

声明了函数或变量,但是未对其进行定义

当头文件中存在声明但未实现匹配定义时,可能会发生 LNK2019。 对于成员函数或 static 数据成员,实现必须包括类范围选择器。 有关示例,请参见 Missing Function Body or Variable

函数声明和函数定义之间的调用约定不同

某些调用约定(__cdecl__stdcall__fastcall__vectorcall)作为修饰名称的一部分进行编码。 确保调用约定是相同的。

符号在 C 文件中定义,但未使用 extern "C" 在 C++ 文件中进行声明

编译为 C 的文件将为符号创建修饰名称,这些名称不同于在 C++ 文件中声明的相同符号的修饰名称,除非使用 extern "C" 修饰符。 确保声明与每个符号的编译链接匹配。 同样,如果在 C 程序将使用的 C++ 文件中定义符号,请在定义中使用 extern "C"

符号定义为 static,并随后在文件外部引用

不同于 C,在 C++ 中,全局常量 (constants) 具有 static 链接。 若要避开此限制,可以在头文件中包含 const 初始化,并在 .cpp 文件中包含该头文件,或者你可以使变量成为非 const 并使用 const 引用来进行访问。

未定义类的 static 成员

static 类成员必须具有唯一的定义,否则将违反单个定义规则。 无法以内联方式定义的 static 类成员必须通过使用其完全限定名称在一个源文件中进行定义。 如果根本没有进行定义,链接器会生成 LNK2019。

生成依赖项仅在解决方案中定义为项目依赖项

在早期版本的 Visual Studio 中,此级别的依赖已足够。 但是,从 Visual Studio 2010 开始,Visual Studio 需要项目到项目引用。 如果你的项目没有项目到项目引用,你可能收到此链接器错误。 添加项目到项目引用以修复此错误。

未定义入口点

应用程序代码必须定义适当的入口点:对于控制台应用程序,为 mainwmain,对于 Windows 应用程序,为 WinMainwWinMain。 有关详细信息,请参阅 main 函数和命令行参数WinMain 函数。 若要使用自定义入口点,请指定 /ENTRY(入口点符号)链接器选项。

通过使用 Windows 应用程序的设置生成控制台应用程序

如果错误消息类似于 function_name 函数中引用的未解析外部 (external) 符号 WinMain,请使用 /SUBSYSTEM:CONSOLE 而不是 /SUBSYSTEM:WINDOWS 进行链接。 有关此设置的详细信息以及如何在 Visual Studio 中设置此属性的说明,请参阅 /SUBSYSTEM(指定子系统)

必须针对与你的代码相同的体系结构,编译链接到代码的库和目标文件。 确保针对与你的项目相同的体系结构编译项目引用的库。 确保 /LIBPATH 或“其他库目录”属性指向为正确体系结构生成的库。

你为在不同源文件中内联的函数使用了不同的编译器选项

使用 .cpp 文件中定义的内联函数并在不同源文件中混合使用函数内联编译器可能会导致 LNK2019。 有关详细信息,请参阅 Function Inlining Problems

在自动变量范围外使用自动变量

自动(函数范围)变量仅可在该函数的范围内使用。 这些变量不可声明为 extern ,也不能在其他源文件中使用。 有关示例,请参见 Automatic (Function Scope) Variables

调用内部函数或将参数类型传递到目标体系结构不支持的内部函数

例如,如果你使用 AVX2 内部函数,但未指定 /ARCH:AVX2 编译器选项,则编译器假定该内部函数是外部 (external) 函数。 编译器不会生成内联指令,而是生成对与内部函数同名的外部 (external) 符号的调用。 当链接器尝试找到此缺失函数的定义时,它会生成 LNK2019。 确保只使用了目标体系结构支持的内部函数和类型。

你将使用和未使用本机 wchar_t 的代码混合在一起

在 Visual Studio 2005 中完成的 C++ 语言一致性工作使 wchar_t 成为默认本机类型。 如果并非所有文件都已经使用相同的 /Zc:wchar_t 设置进行编译,那么类型引用可能无法解析为兼容类型。 确保所有库中的 wchar_t 类型和对象文件都兼容。 从 wchar_t typedef 进行更新,或者在编译时使用一致的 /Zc:wchar_t 设置

使用 Visual Studio 2015 之前的 Visual Studio 版本生成的 static 库在与 UCRT 链接时可能出现 LNK2019 错误。 UCRT 头文件 <stdio.h><conio.h><wchar.h> 现在将许多 *printf**scanf* 变体定义为 inline 函数。 内联函数通过一小部分通用函数实现。 标准 UCRT 库中不支持单独导出内联函数,这些库仅导出通用函数。 可通过多种方法来解决此问题。 建议使用当前版本的 Visual Studio 重新生成旧版库。 请确保库代码将标准标头用于导致错误的 *printf**scanf* 函数的定义。 如果无法重新生成旧版库,另一个选项是将 legacy_stdio_definitions.lib 添加到所链接的库的列表。 此库文件为 UCRT 标头中的内联函数 *printf**scanf* 提供符号。 有关详细信息,请参阅潜在的升级问题概述中的“库”部分

第三方库问题和 vcpkg

如果在尝试将第三方库配置为生成的一部分时看到此错误,请考虑使用 vcpkg。 vcpkg 是一个 C++ 包管理器,它使用现有的 Visual Studio 工具来安装和生成库。 vcpkg 支持一个庞大且不断增长的第三方库列表。 它将成功生成所需的所有配置属性和依赖项设置为项目的一部分。

诊断工具

有时很难判断为什么链接器找不到特定的符号定义。 问题通常是你没有在生成中添加包含定义的代码。 或者,生成选项为外部 (external) 符号创建了不同的修饰名称。 有许多工具和选项可以帮助你诊断 LNK2019 错误。

  • /VERBOSE 链接器选项可以帮助你确定链接器引用的文件。 此选项可以帮助你验证生成中是否包括了含有符号定义的文件。

  • DUMPBIN 实用工具的 /EXPORTS/SYMBOLS 选项可帮助你发现 .dll 和对象或库文件中定义的符号。 确保导出的修饰名称与链接器搜索的修饰名称匹配。

  • UNDNAME 实用工具可以显示修饰名称的等效未修饰外部 (external) 符号。

示例

以下是一些导致 LNK2019 错误的代码示例,以及关于如何修复错误的信息。

声明了符号,但是未对其进行定义

在此示例中,声明了外部 (external) 变量,但未对其进行定义:

// LNK2019.cpp
// Compile by using: cl /EHsc /W4 LNK2019.cpp
// LNK2019 expected
extern char B[100];   // B isn't available to the linker
int main() {
   B[0] = ' ';   // LNK2019
}

以下是另一个示例,其中变量和函数被声明为 extern 但未提供定义:

// LNK2019c.cpp
// Compile by using: cl /EHsc LNK2019c.cpp
// LNK2019 expected
extern int i;
extern void g();
void f() {
   i++;
   g();
}
int main() {}

除非在生成所含的一个文件中定义了 ig,否则链接器会生成 LNK2019。 你可以通过将包含定义的源代码文件作为编译的一部分包括在其中来修复错误。 或者,你可以将包含定义的 .obj 文件或 .lib 文件传递给链接器。

声明了 static 数据成员,但是未对其进行定义

声明了 static 数据成员,但未对其进行定义时也可能发生 LNK2019。 以下示例生成 LNK2019,并演示如何修复此错误。

// LNK2019b.cpp
// Compile by using: cl /EHsc LNK2019b.cpp
// LNK2019 expected
struct C {
   static int s;
};

// Uncomment the following line to fix the error.
// int C::s;

int main() {
   C c;
   C::s = 1;
}

声明参数与定义不匹配

调用函数模板的代码必须拥有匹配的函数模板声明。 声明必须包括与定义相同的模板参数。 以下示例在用户定义的运算符上生成 LNK2019,并演示如何修复此错误。

// LNK2019e.cpp
// compile by using: cl /EHsc LNK2019e.cpp
// LNK2019 expected
#include <iostream>
using namespace std;

template<class T> class
Test {
   // The operator<< declaration doesn't match the definition below:
   friend ostream& operator<<(ostream&, Test&);
   // To fix, replace the line above with the following:
   // template<typename T> friend ostream& operator<<(ostream&, Test<T>&);
};

template<typename T>
ostream& operator<<(ostream& os, Test<T>& tt) {
   return os;
}

int main() {
   Test<int> t;
   cout << "Test: " << t << endl;   // LNK2019 unresolved external
}

wchar_t 类型定义不一致

此示例创建具有使用 WCHAR 的导出的 DLL,其解析为 wchar_t

// LNK2019g.cpp
// compile with: cl /EHsc /LD LNK2019g.cpp
#include "windows.h"
// WCHAR resolves to wchar_t
__declspec(dllexport) void func(WCHAR*) {}

下一示例使用上一示例中的 DLL,并生成 LNK2019,因为 unsigned short*WCHAR* 的类型不同。

// LNK2019h.cpp
// compile by using: cl /EHsc LNK2019h LNK2019g.lib
// LNK2019 expected
__declspec(dllimport) void func(unsigned short*);

int main() {
   func(0);
}

若要解决此错误,请将 unsigned short 更改为 wchar_tWCHAR,或使用 /Zc:wchar_t- 编译 LNK2019g.cpp。

另请参阅

有关 LNK2019、LNK2001 和 LNK1120 错误的可能原因和解决方案的详细信息,请参阅 Stack Overflow 问题:What is an undefined reference/unresolved external symbol error and how do I fix it?