什么是未定义的参考/未解析的外部符号错误?常见原因是什么?如何解决/预防?
当前回答
在链接共享库时,请确保未隐藏使用的符号。
gcc的默认行为是所有符号都可见。但是,当使用选项-fvisibility=hidden构建转换单元时,只有标记为__attribute__((可见性(“默认”))的函数/符号在生成的共享对象中是外部的。
您可以通过调用以下命令来检查要查找的符号是否为外部符号:
# -D shows (global) dynamic symbols that can be used from the outside of XXX.so
nm -D XXX.so | grep MY_SYMBOL
隐藏/本地符号用小写符号类型的nm表示,例如t而不是代码段的“t”:
nm XXX.so
00000000000005a7 t HIDDEN_SYMBOL
00000000000005f8 T VISIBLE_SYMBOL
您还可以使用nm和选项-C来定义名称(如果使用了C++)。
与Windows DLL类似,可以使用define标记公共函数,例如DLL_public定义为:
#define DLL_PUBLIC __attribute__ ((visibility ("default")))
DLL_PUBLIC int my_public_function(){
...
}
大致对应于Windows的/MSVC版本:
#ifdef BUILDING_DLL
#define DLL_PUBLIC __declspec(dllexport)
#else
#define DLL_PUBLIC __declspec(dllimport)
#endif
有关可见性的更多信息可以在gcc wiki上找到。
当使用-fvisibility=hidden编译翻译单元时,生成的符号仍然具有外部链接(以大写符号类型显示,单位为nm),如果对象文件成为静态库的一部分,则可以毫无问题地用于外部链接。只有当对象文件链接到共享库中时,链接才会变为本地链接。
要查找对象文件中隐藏的符号,请运行:
>>> objdump -t XXXX.o | grep hidden
0000000000000000 g F .text 000000000000000b .hidden HIDDEN_SYMBOL1
000000000000000b g F .text 000000000000000b .hidden HIDDEN_SYMBOL2
其他回答
这是每个VC++程序员一再看到的最令人困惑的错误消息之一。让我们先把事情弄清楚。
A.什么是符号?简而言之,符号就是名称。它可以是变量名、函数名、类名、typedef名,或者除了那些属于C++语言的名称和符号之外的任何名称和符号。它由用户定义或由依赖库(另一个用户定义的)引入。
B.什么是外部的?在VC++中,每个源文件(.cpp、.c等)都被视为一个翻译单元,编译器一次编译一个单元,并为当前翻译单元生成一个目标文件(.obj)。(请注意,此源文件包含的每个头文件都将被预处理,并将被视为此翻译单元的一部分)翻译单元中的所有内容都被视为内部内容,其他所有内容都视为外部内容。在C++中,可以使用关键字extern、__declspec(dllimport)等引用外部符号。
C.什么是“决心”?Resolve是一个链接时间术语。在链接时,链接器尝试为对象文件中无法在内部找到其定义的每个符号找到外部定义。此搜索过程的范围包括:
编译时生成的所有对象文件显式或隐式的所有库(.lib)指定为此生成应用程序的附加依赖项。
此搜索过程称为解析。
D.最后,为什么是未解决的外部符号?如果链接器找不到内部没有定义的符号的外部定义,则会报告“未解决的外部符号”错误。
E.LNK2019的可能原因:未解决的外部符号错误。我们已经知道,此错误是由于链接器未能找到外部符号的定义所致,可能的原因如下:
定义已存在
例如,如果我们在.cpp中定义了一个名为foo的函数:
int foo()
{
return 0;
}
在b.cpp中,我们希望调用函数foo,因此我们添加
void foo();
要声明函数foo(),并在另一个函数体中调用它,请使用bar():
void bar()
{
foo();
}
现在,当您构建此代码时,您将收到一个LNK2019错误,抱怨foo是一个未解析的符号。在本例中,我们知道foo()的定义在.cpp中,但与我们调用的定义不同(返回值不同)。这就是定义存在的情况。
定义不存在
如果我们想调用库中的某些函数,但导入库没有添加到项目设置的附加依赖项列表中(设置自:项目|财产|配置财产|链接器|输入|附加依赖项)。现在链接器将报告LNK2019,因为当前搜索范围中不存在该定义。
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,请确保未设置定义,和/或在项目中使用了多字符设置,并始终应用。
不要忘记在“发布”和“调试”版本之间保持一致。
如果所有其他操作都失败,请重新编译。
最近,我只需重新编译有问题的文件,就可以消除Visual Studio 2012中未解决的外部错误。当我重新构建时,错误消失了。
当两个(或多个)库具有循环依赖关系时,通常会发生这种情况。库A尝试使用B.lib中的符号,库B尝试使用A.lib中的字符。两者都不存在。当您尝试编译A时,链接步骤将失败,因为它找不到B.lib。将生成A.lib,但不会生成dll。然后编译B,这将成功并生成B.lib。重新编译A现在可以工作了,因为现在找到了B.lib。
正在添加模板。。。
给定带有友元运算符(或函数)的模板类型的代码片段;
template <typename T>
class Foo {
friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
};
运算符<<被声明为非模板函数。对于与Foo一起使用的每种类型T,都需要有一个非模板运算符<<。例如,如果声明了一个类型Foo<int>,那么必须有如下的运算符实现:;
std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
由于它未实现,链接器无法找到它并导致错误。
要更正此问题,可以在Foo类型之前声明一个模板运算符,然后将适当的实例化声明为友元。语法有点尴尬,但看起来如下:;
// forward declare the Foo
template <typename>
class Foo;
// forward declare the operator <<
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&);
template <typename T>
class Foo {
friend std::ostream& operator<< <>(std::ostream& os, const Foo<T>& a);
// note the required <> ^^^^
// ...
};
template <typename T>
std::ostream& operator<<(std::ostream&, const Foo<T>&)
{
// ... implement the operator
}
上述代码将运算符的友谊限制为Foo的相应实例化,即运算符<<<int>实例化被限制为访问Foo<int>的实例化的私有成员。
备选方案包括:;
允许友谊扩展到模板的所有实例化,如下所示;模板<typename T>Foo类{模板<typename T1>朋友std::ostream&operator<<(std::estream&os,const Foo<T1>/a);// ...};或者,运算符<<的实现可以在类定义内内联完成;模板<typename T>Foo类{朋友std::ostream&operator<<(std::estream&os,const Foo&a){ /*...*/ }// ...};
注意,当运算符(或函数)的声明仅出现在类中时,该名称不适用于“普通”查找,仅适用于cppreference中的依赖于参数的查找;
首先在类或类模板X中的友元声明中声明的名称将成为X最内部封闭命名空间的成员,但除非在命名空间范围内提供了匹配声明,否则无法进行查找(考虑X的依赖于参数的查找除外)。。。
cppreference和C++常见问题解答中有关于模板朋友的进一步阅读。
显示上述技术的代码列表。
作为失败代码示例的旁注;g++警告如下
警告:friend声明“std::ostream&operator<<(…)”声明非模板函数[-Wnon-template friend]注意:(如果这不是您想要的,请确保函数模板已经声明,并在此处的函数名称后面添加<>)
因为当涉及到链接器错误时,人们似乎都在关注这个问题,所以我将在这里添加这个问题。
GCC 5.2.0中出现链接器错误的一个可能原因是现在默认选择了新的libstdc++库ABI。
如果您在std::__cxx11命名空间或标记[abi:cx11]中获得了有关符号未定义引用的链接器错误,则可能表明您正在尝试将使用_GLIBCXX_USE_cxx11_abi宏的不同值编译的对象文件链接在一起。这通常发生在链接到使用旧版本GCC编译的第三方库时。如果无法使用新的ABI重建第三方库,则需要使用旧的ABI重新编译代码。
因此,如果在5.1.0之后切换到GCC时突然出现链接器错误,这将是一件值得检查的事情。
推荐文章
- 转换Python程序到C/ c++代码?
- 使用goto有什么问题?
- 度量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?