什么是未定义的参考/未解析的外部符号错误?常见原因是什么?如何解决/预防?
当前回答
此外,如果您正在使用第三方库,请确保您拥有正确的32/64位二进制文件
其他回答
如果所有其他操作都失败,请重新编译。
最近,我只需重新编译有问题的文件,就可以消除Visual Studio 2012中未解决的外部错误。当我重新构建时,错误消失了。
当两个(或多个)库具有循环依赖关系时,通常会发生这种情况。库A尝试使用B.lib中的符号,库B尝试使用A.lib中的字符。两者都不存在。当您尝试编译A时,链接步骤将失败,因为它找不到B.lib。将生成A.lib,但不会生成dll。然后编译B,这将成功并生成B.lib。重新编译A现在可以工作了,因为现在找到了B.lib。
未能链接到适当的库/对象文件或编译实现文件
通常,每个翻译单元都会生成一个包含该翻译单元中定义的符号定义的对象文件。要使用这些符号,必须链接这些对象文件。
在gcc下,您可以指定要在命令行中链接在一起的所有对象文件,或者一起编译实现文件。
g++ -o test objectFile1.o objectFile2.o -lLibraryName
-我。。。必须位于任何.o/.c/.cpp文件的右侧。
这里的libraryName只是库的裸名,没有特定于平台的添加。例如,在Linux上,库文件通常被称为libfoo.So,但您只能编写-lfo。在Windows上,相同的文件可能被称为foo.lib,但您将使用相同的参数。您可能需要添加目录,在该目录中可以使用-Lûdirectory›找到这些文件。确保不要在-l或-l后面写空格。
对于Xcode:添加用户标题搜索路径->添加库搜索路径->将实际的库引用拖放到项目文件夹中。
在MSVS下,添加到项目中的文件会自动将其对象文件链接在一起,并生成一个lib文件(常见用法)。要在单独的项目中使用符号,您需要需要在项目设置中包含lib文件。这是在项目财产的链接器部分的Input->Additional Dependencies中完成的。(指向lib文件的路径应为在Linker->General->Additional Library Directories中添加)当使用随lib文件提供的第三方库时,失败通常会导致错误。
还可能发生忘记将文件添加到编译中的情况,在这种情况下,不会生成对象文件。在gcc中,您可以将文件添加到命令行。在MSVS中,将文件添加到项目将使其自动编译(尽管文件可以手动从构建中单独排除)。
在Windows编程中,未链接必要库的标志是未解析符号的名称以__imp_开头。在文档中查找函数的名称,它应该指出您需要使用哪个库。例如,MSDN将信息放在名为“库”的部分中每个函数底部的框中。
需要考虑的一些拼写错误:(我作为初学者经常遇到)
如果您使用的是类:请检查您是否没有在定义函数的cpp文件中的函数名之前忘记“classname::”。如果使用forward声明:请确保声明正确的类型。例如:如果要转发声明“结构”,请使用“结构”而不是“类”。
对的未定义引用WinMain@16或类似的“不寻常”main()入口点引用(特别是对于visual studio)。
您可能错过了使用实际IDE选择正确的项目类型。IDE可能希望将例如Windows应用程序项目绑定到这样的入口点函数(如上面缺失的引用中所指定的),而不是通常使用的int main(int argc,char**argv);签名
如果IDE支持普通控制台项目,您可能希望选择此项目类型,而不是windows应用程序项目。
下面是从真实世界问题中更详细地处理的案例1和案例2。
什么是“未定义的引用/未解析的外部符号”
我将尝试解释什么是“未定义的引用/未解析的外部符号”。
注意:我使用的是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
好的,我们破解它:)
因此,当链接器在对象文件中找不到全局符号时,就会发生“未定义的引用/未解析的外部符号错误”。