什么是未定义的参考/未解析的外部符号错误?常见原因是什么?如何解决/预防?
当前回答
对的未定义引用WinMain@16或类似的“不寻常”main()入口点引用(特别是对于visual studio)。
您可能错过了使用实际IDE选择正确的项目类型。IDE可能希望将例如Windows应用程序项目绑定到这样的入口点函数(如上面缺失的引用中所指定的),而不是通常使用的int main(int argc,char**argv);签名
如果IDE支持普通控制台项目,您可能希望选择此项目类型,而不是windows应用程序项目。
下面是从真实世界问题中更详细地处理的案例1和案例2。
其他回答
编译器/IDE中的错误
我最近遇到了这个问题,结果发现这是Visual Studio Express 2013中的一个错误。我不得不从项目中删除一个源文件,然后重新添加它以克服错误。
如果您认为这可能是编译器/IDE中的错误,请尝试以下步骤:
清理项目(一些IDE可以选择这样做,您也可以手动删除对象文件)尝试启动新项目,从原始代码复制所有源代码。
什么是“未定义的引用/未解析的外部符号”
我将尝试解释什么是“未定义的引用/未解析的外部符号”。
注意:我使用的是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;
}
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);
}
生成对象文件
$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o
在汇编程序阶段之后,我们有一个对象文件,其中包含要导出的任何符号。看看这些符号
$ readelf --symbols src1.o
Num: Value Size Type Bind Vis Ndx Name
5: 0000000000000000 4 OBJECT LOCAL DEFAULT 4 _ZL14local_var_name # [1]
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 global_var_name # [2]
我拒绝了输出中的一些行,因为它们无关紧要
因此,我们看到要导出的以下符号。
[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable
src2.cpp不导出任何内容,我们没有看到它的符号
链接我们的对象文件
$ g++ src1.o src2.o -o prog
并运行它
$ ./prog
123
Linker看到导出的符号并将其链接起来
// src2.cpp
extern "C" int printf (const char*, ...);
extern int global_var_name;
extern int local_var_name;
void print ()
{
printf("%d%d\n", global_var_name, local_var_name);
}
并重建对象文件
$ g++ -c src2.cpp -o src2.o
好的(没有错误),因为我们只构建对象文件,链接还没有完成。尝试链接
$ g++ src1.o src2.o -o prog
src2.o: In function `print()':
src2.cpp:(.text+0x6): undefined reference to `local_var_name'
collect2: error: ld returned 1 exit status
发生这种情况是因为我们的local_var_name是静态的,即它对其他模块不可见。现在更深入。获取翻译阶段输出
$ g++ -S src1.cpp -o src1.s
// src1.s
look src1.s
.file "src1.cpp"
.local _ZL14local_var_name
.comm _ZL14local_var_name,4,4
.globl global_var_name
.data
.align 4
.type global_var_name, @object
.size global_var_name, 4
global_var_name:
.long 123
.text
.globl main
.type main, @function
main:
; assembler code, not interesting for us
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu 4.8.2-19ubuntu1) 4.8.2"
.section .note.GNU-stack,"",@progbits
所以,我们看到local_var_name没有标签,这就是链接器找不到它的原因。但我们是黑客:),我们可以修复它。在文本编辑器中打开src1.s并更改
.local _ZL14local_var_name
.comm _ZL14local_var_name,4,4
to
.globl local_var_name
.data
.align 4
.type local_var_name, @object
.size local_var_name, 4
local_var_name:
.long 456789
也就是说,你应该像下面这样
.file "src1.cpp"
.globl local_var_name
.data
.align 4
.type local_var_name, @object
.size local_var_name, 4
local_var_name:
.long 456789
.globl global_var_name
.align 4
.type global_var_name, @object
.size global_var_name, 4
global_var_name:
.long 123
.text
.globl main
.type main, @function
main:
; ...
我们已经更改了localvarname的可见性,并将其值设置为456789。尝试从中构建对象文件
$ g++ -c src1.s -o src2.o
好,请参阅readelf输出(符号)
$ readelf --symbols src1.o
8: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 local_var_name
现在local_var_name具有绑定GLOBAL(以前是local)
link
$ g++ src1.o src2.o -o prog
并运行它
$ ./prog
123456789
好的,我们破解它:)
因此,当链接器在对象文件中找不到全局符号时,就会发生“未定义的引用/未解析的外部符号错误”。
我的例子:
头文件
class GameCharacter : public GamePart
{
private:
static vector<GameCharacter*> characterList;
...
}
.cpp文件:
vector<GameCharacter*> characterList;
这产生了“未定义”加载程序错误,因为“characterList”被声明为静态成员变量,但被定义为全局变量。
我加上这个是因为——虽然其他人在一长串需要注意的事情中列出了这个案例——但这个列表并没有给出示例。这是一个更值得寻找的例子,尤其是在C++中。
修复方法是向全局变量添加限定以定义静态数据成员:
vector<GameCharacter*> GameCharacter::characterList;
同时保持收割台相同。
UNICODE定义不一致
Windows UNICODE构建时,TCHAR等被定义为wchar_t等。如果不使用UNICODE进行构建,则TCHAR被定义为char等。这些UNICODE和_UNICODE定义会影响所有“t”字符串类型;LPTSTR、LPCTSTR及其麋鹿。
构建一个定义了UNICODE的库,并试图将其链接到未定义UNICODE项目中,将导致链接器错误,因为TCHAR的定义将不匹配;char与wchar_t。
错误通常包括一个带有char或wchar_t派生类型的值的函数,这些类型也可能包括std::basic_string<>等。浏览代码中受影响的函数时,通常会引用TCHAR或std::basic_string<TCHAR>等。这是一个信号,表明代码最初用于UNICODE和多字节字符(或“窄”)构建。
要更正此问题,请使用UNICODE(和_UNICODE)的一致定义构建所有必需的库和项目。
这可以通过以下两种方式实现:;#定义UNICODE#定义UNICODE或在项目设置中;项目财产>常规>项目默认值>字符集或在命令行上;/DUNICODE/D-UNICODE
如果不打算使用UNICODE,请确保未设置定义,和/或在项目中使用了多字符设置,并始终应用。
不要忘记在“发布”和“调试”版本之间保持一致。
不同的架构
您可能会看到这样的消息:
library machine type 'x64' conflicts with target machine type 'X86'
在这种情况下,这意味着可用符号用于不同于您正在编译的体系结构。
在Visual Studio上,这是由于错误的“平台”,您需要选择正确的平台或安装正确版本的库。
在Linux上,这可能是由于错误的库文件夹(例如,使用lib而不是lib64)。
在MacOS上,可以选择在同一文件中传送两种体系结构。可能是链接希望两个版本都存在,但只有一个版本存在。也可能是库所在的lib/lib64文件夹错误。
推荐文章
- 度量c++中函数的执行时间
- 我如何类型定义一个函数指针与c++ 11使用语法?
- cbegin/cend背后的原因是什么?
- 如何在Eclipse CDT中启用c++ 11/ c++ 0x支持?
- 将string (or char*)转换为wstring (or wchar_t*)
- 如何停止c++控制台应用程序从退出立即?
- 有没有替换Windows (Visual C)的unistd.h ?
- 目标文件中无法解析的外部符号
- Lambda捕获作为const引用?
- 如何从c风格数组初始化std::vector ?
- 是std::vector复制对象push_back?
- 为什么在c++ 11中使用非成员begin和end函数?
- 如何通过参数和重定向stdin从一个文件到程序运行在gdb?
- 为什么两个不同的概念都叫“堆”?
- 为什么这个结合赋值和相等检查的if语句返回true?