本文翻译自:What is an undefined reference/unresolved external symbol error and how do I fix it?
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. 随意编辑/添加自己的。
#1楼
参考:https://stackoom.com/question/ql1U/什么是未定义的引用-未解决的外部符号错误-如何解决
#2楼
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] . 翻译的语法规则中的优先级由以下阶段指定[请参见脚注] 。
- 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] [SNIP]
- 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] [SNIP]
- The source file is decomposed into preprocessing tokens (2.5) and sequences of white-space characters (including comments). 源文件被分解为预处理令牌(2.5)和空白字符序列(包括注释)。 [SNIP] [SNIP]
- Preprocessing directives are executed, macro invocations are expanded, and _Pragma unary operator expressions are executed. 执行预处理指令,扩展宏调用,并执行_Pragma一元运算符表达式。 [SNIP] [SNIP]
- 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] [SNIP]
- Adjacent string literal tokens are concatenated. 相邻的字符串文字标记是串联在一起的。
- White-space characters separating tokens are no longer significant. 分隔标记的空格字符不再重要。 Each preprocessing token is converted into a token. 每个预处理令牌都将转换为令牌。 (2.7). (2.7)。 The resulting tokens are syntactically and semantically analyzed and translated as a translation unit. 对生成的令牌进行语法和语义分析,并将其作为翻译单元进行翻译。 [SNIP] [SNIP]
- Translated translation units and instantiation units are combined as follows: [SNIP] 翻译的翻译单元和实例化单元的组合如下: [SNIP]
- 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. [脚注]尽管实际上不同的阶段可能会折叠在一起,但实现方式必须表现得好像这些单独的阶段一样。
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 a in a.cpp . 假设您在a.cpp定义了符号a 。 Now, b.cpp declared that symbol and used it. 现在, b.cpp 声明了该符号并使用了它。 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). 链接阶段负责查找符号并将其正确链接到b.cpp (实际上是使用它的对象或库)。
If you're using Microsoft Visual Studio, you'll see that projects generate .lib files. 如果您使用的是Microsoft Visual Studio,则会看到项目会生成.lib文件。 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). 将根据链接的库解析导入的符号,并为使用该.lib的库(如果有)提供导出的符号。
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 . 常见错误消息是Microsoft Visual Studio error LNK2001 , error LNK1120 , error LNK2019和GCC的 undefined reference to symbolName的 undefined reference to 。
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: 常见原因包括:
- Failure to link against appropriate libraries/object files or compile implementation files 无法链接到适当的库/目标文件或编译实现文件
- Declared and undefined variable or function. 声明和未定义的变量或函数。
- Common issues with class-type members 类成员的常见问题
- Template implementations not visible. 模板实现不可见。
- Symbols were defined in a C program and used in C++ code. 符号在C程序中定义,并在C ++代码中使用。
- Incorrectly importing/exporting methods/classes across modules/dll. 跨模块/ dll错误地导入/导出方法/类。 (MSVS specific) (特定于MSVS)
- Circular library dependency 循环库依赖
- undefined reference to `WinMain@16' 未定义对“ WinMain @ 16”的引用
- Interdependent library order 相互依赖的库顺序
- Multiple source files of the same name 具有相同名称的多个源文件
- Mistyping or not including the .lib extension when using the
#pragma(Microsoft Visual Studio) 使用#pragma(Microsoft Visual Studio)时是否格式错误或不包括.lib扩展名 - Problems with template friends 模板朋友的问题
- Inconsistent
UNICODEdefinitionsUNICODE定义不一致
#3楼
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. 在gcc下,您将指定所有要在命令行中链接在一起的目标文件,或者将实现文件编译在一起。
g++ -o test objectFile1.o objectFile2.o -lLibraryName
The libraryName here is just the bare name of the library, without platform-specific additions. 这里的libraryName只是库的裸名,没有特定于平台的添加。 So eg on Linux library files are usually called libfoo.so but you'd only write -lfoo . 因此,例如在Linux上,库文件通常称为libfoo.so但您只写-lfoo 。 On Windows that same file might be called foo.lib , but you'd use the same argument. 在Windows上,该文件可能称为foo.lib ,但您将使用相同的参数。 You might have to add the directory where those files can be found using -L‹directory› . 您可能必须使用-L‹directory›添加可以在其中找到这些文件的-L‹directory› 。 Make sure to not write a space after -l or -L . 确保不要在-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 lib file would be generated (in common usage). 在MSVS下 ,添加到项目中的文件会自动将其目标文件链接在一起,并且会生成一个lib文件(通常使用)。 To use the symbols in a separate project, you'd need to include the lib files in the project settings. 要在单独的项目中使用这些符号,您需要在项目设置中包含lib文件。 This is done in the Linker section of the project properties, in Input -> Additional Dependencies . 这是在项目属性的“链接器”部分的“ 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. ( 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 gcc you'd add the files to the command line. 在gcc中,您可以将文件添加到命令行中。 In MSVS adding the file to the project will make it compile it automatically (albeit files can, manually, be individually excluded from the build). 在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_ . 在Windows编程中,您没有链接必要库的迹象表明,未解析符号的名称以__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". 例如,MSDN将信息放在每个函数底部“方框”中的框中。
#4楼
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: 例如,您的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. 确保每个细节都匹配。
#5楼
Class members: 班级成员:
A pure virtual destructor 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. 发生这种情况是因为在隐式销毁对象时调用了基类析构函数,因此需要定义。
virtual methods must either be implemented or defined as pure. virtual方法必须实现或定义为纯方法。
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: 这类似于没有定义的非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- virtual class 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 inline to 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() {}
static data 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 static const data member of integral or enumeration type within the class definition; 可以在类定义中为整数或枚举类型的static const数据成员提供初始化程序。 however, odr-use of this member will still require a namespace scope definition as described above. 但是,对该成员的odr-use仍然需要如上所述的名称空间范围定义。 C++11 allows initialization inside the class for all static const data members. C ++ 11允许在类内部对所有static const数据成员进行初始化。
#6楼
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. 如果必须分开实现,通常的解决方法是在声明模板的标头末尾包含一个impl文件。 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. 要解决此问题,必须将X::foo的定义移至头文件或使用该文件的翻译单元可见的位置。
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 . 有关进一步的说明和其他可能的解决方案(显式实例化),请参阅此问题和答案 。
未定义的引用/未解决的外部符号错误通常发生在编译的链接阶段,意味着编译器找不到在源代码中声明的符号的实现。常见原因包括未链接所需的库/目标文件、未定义的变量或函数、类成员的缺失实现、模板实现不可见等。解决方法涉及确保正确链接库、提供函数和变量的定义、避免名字空间混淆、在正确位置定义模板等。
1637

被折叠的 条评论
为什么被折叠?



