什么是未定义的引用/未解析的外部符号错误?我如何修复它?

tjrkku2a  于 2022-09-26  发布在  其他
关注(0)|答案(30)|浏览(355)

什么是未定义的参考/未解析的外部符号错误?常见的原因是什么?如何修复/预防它们?

jhdbpxl9

jhdbpxl916#

需要为新的工具集版本更新Visual Studio NuGet包

我在尝试将libpng链接到Visual Studio 2013时遇到了这个问题。问题是,程序包文件只有用于Visual Studio2010和2012的库。

正确的解决方案是希望开发人员发布一个更新的包,然后进行升级,但这对我来说很管用,我破解了VS2013的一个额外设置,指向VS2012库文件。

我通过在该文件中找到packagename\build\native\packagename.targets并复制所有v110节来编辑包(在解决方案目录内的packages文件夹中)。我将**条件字段中的v110更改为v120**非常小心地将文件名路径全部保留为v110。这只是允许Visual Studio 2013链接到2012年的库,在这种情况下,它起作用了。

ffx8fchx

ffx8fchx17#

编译C++程序分几个步骤进行,如2.2(credits to Keith Thompson for the reference)所指定:
翻译的语法规则之间的优先顺序由以下阶段*[见脚注]*规定。

1.如有必要,以实现定义的方式将物理源文件字符Map到基本源字符集(引入换行符作为行尾指示符)。[剪报]
1.删除紧跟换行符的反斜杠字符()的每个示例,拼接物理源行以形成逻辑源行。[剪报]
1.源文件被分解为预处理标记(2.5)和空白字符序列(包括注解)。[剪报]
1.执行预处理指令,展开宏调用,执行_Pragma一元运算符表达式。[剪报]
1.将字符文字或字符串文字中的每个源字符集成员,以及字符文字或非原始字符串文字中的每个转义序列和通用字符名称转换为执行字符集的相应成员;[Snip]
1.连接相邻的字符串文字标记。
1.空格字符分隔令牌不再重要。每个预处理令牌被转换为令牌。(2.7)。得到的标记被作为翻译单位进行句法和语义分析和翻译。[剪报]
1.翻译后的翻译单元和示例化单元组合如下:[Snip]
1.解析所有外部实体引用。链接库组件以满足对未在当前转换中定义的实体的外部参照。所有这样的翻译器输出都被收集到一个程序映像中,其中包含在其执行环境中执行所需的信息。(重点挖掘)

  • [脚注]*实现必须表现为这些单独的阶段发生,尽管在实践中不同的阶段可能会合并在一起。

指定的错误发生在编译的最后阶段,通常称为链接。这基本上意味着您将一组实现文件编译成目标文件或库,现在您想让它们协同工作。

假设您在a.cpp中定义了符号a。现在,b.cpp声明了该符号并使用了它。在链接之前,它简单地假设该符号是在某个地方定义的,但它并不关心在哪里定义的。链接阶段负责查找符号并将其正确链接到b.cpp(实际上是链接到使用它的对象或库)。

如果您使用的是Microsoft Visual Studio,您将看到项目生成.lib文件。其中包含一个导出符号表和一个导入符号表。导入的符号将根据链接所针对的库进行解析,并为使用该.lib(如果有)的库提供导出的符号。

对于其他编译器/平台也存在类似的机制。

常见的错误信息有:Microsoft Visual Studioerror LNK2001error LNK1120error LNK2019GCCundefined reference tosymbol Name

代码:

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;
}

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

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

常见原因包括:

5tmbdcev

5tmbdcev18#

微软提供了一个#pragma来在链接时引用正确的库;


# pragma comment(lib, "libname.lib")

除了包括库目录的库路径外,这还应该是库的全名。

mkh04yzy

mkh04yzy19#

另外,如果您使用的是第三方库,请确保您拥有正确的32/64位二进制文件

bweufnob

bweufnob20#

未定义对WinMain@16或类似“不寻常”main()入口点引用的引用**(尤其是对于visual-studio)。

您可能没有使用实际的IDE选择正确的项目类型。例如,IDE可能希望将Windows应用程序项目绑定到这样的入口点函数(如上面缺失的引用中所指定的),而不是通常使用的int main(int argc, char**argv);签名。

如果您的IDE支持普通控制台项目,您可能希望选择此项目类型,而不是Windows应用程序项目。

下面是一个“真实世界”问题中更详细地处理的案例1和case2

b4lqfgs4

b4lqfgs421#

跨模块/DLL错误地导入/导出方法/类(编译器特定)。

MSVS要求您指定要使用__declspec(dllexport)__declspec(dllimport)输出和输入的符号。

这种双重功能通常通过使用宏获得:


# ifdef THIS_MODULE

# define DLLIMPEXP __declspec(dllexport)

# else

# define DLLIMPEXP __declspec(dllimport)

# endif

THIS_MODULE将仅在导出函数的模块中定义。如此一来,宣言:

DLLIMPEXP void foo();

扩展到

__declspec(dllexport) void foo();

并通知编译器导出该函数,因为当前模块包含其定义。当在不同的模块中包含声明时,它将扩展为

__declspec(dllimport) void foo();

并告诉编译器该定义位于您链接的某个库中(另请参阅1))。

您可以类似于导入/导出类:

class DLLIMPEXP X
{
};
b1payxdu

b1payxdu22#

This is one of most confusing error messages that every VC++ programmers have seen time and time again. Let’s make things clarity first.

**A. What is symbol?**In short, a symbol is a name. It can be a variable name, a function name, a class name, a typedef name, or anything except those names and signs that belong to C++ language. It is user defined or introduced by a dependency library (another user-defined).

**B. What is external?**In VC++, every source file (.cpp,.c,etc.) is considered as a translation unit, the compiler compiles one unit at a time, and generate one object file(.obj) for the current translation unit. (Note that every header file that this source file included will be preprocessed and will be considered as part of this translation unit)Everything within a translation unit is considered as internal, everything else is considered as external. In C++, you may reference an external symbol by using keywords like extern, __declspec (dllimport) and so on.

**C. What is “resolve”?**Resolve is a linking-time term. In linking-time, linker attempts to find the external definition for every symbol in object files that cannot find its definition internally. The scope of this searching process including:

  • All object files that generated in compiling time
  • All libraries (.lib) that are either explicitly or implicitly specified as additional dependencies of this building application.

This searching process is called resolve.

**D. Finally, why Unresolved External Symbol?**If the linker cannot find the external definition for a symbol that has no definition internally, it reports an Unresolved External Symbol error.

E. Possible causes of LNK2019: Unresolved External Symbol error. We already know that this error is due to the linker failed to find the definition of external symbols, the possible causes can be sorted as:

  1. Definition exists

For example, if we have a function called foo defined in a.cpp:

int foo()
{
    return 0;
}

In b.cpp we want to call function foo, so we add

void foo();

to declare function foo(), and call it in another function body, say bar():

void bar()
{
    foo();
}

Now when you build this code you will get a LNK2019 error complaining that foo is an unresolved symbol. In this case, we know that foo() has its definition in a.cpp, but different from the one we are calling(different return value). This is the case that definition exists.

  1. Definition does not exist

If we want to call some functions in a library, but the import library is not added into the additional dependency list (set from: Project | Properties | Configuration Properties | Linker | Input | Additional Dependency) of your project setting. Now the linker will report a LNK2019 since the definition does not exist in current searching scope.

eoigrqb6

eoigrqb623#

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. 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.

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.

72qzrwbm

72qzrwbm24#

If all else fails, recompile.

I was recently able to get rid of an unresolved external error in Visual Studio 2012 just by recompiling the offending file. When I re-built, the error went away.

This usually happens when two (or more) libraries have a cyclic dependency. Library A attempts to use symbols in B.lib and library B attempts to use symbols from A.lib. Neither exist to start off with. When you attempt to compile A, the link step will fail because it can't find B.lib. A.lib will be generated, but no dll. You then compile B, which will succeed and generate B.lib. Re-compiling A will now work because B.lib is now found.

new9mtju

new9mtju25#

Symbols were defined in a C program and used in C++ code.

The function (or variable) void foo() was defined in a C program and you attempt to use it in a C++ program:

void foo();
int main()
{
    foo();
}

The C++ linker expects names to be mangled, so you have to declare the function as:

extern "C" void foo();
int main()
{
    foo();
}

Equivalently, instead of being defined in a C program, the function (or variable) void foo() was defined in C++ but with C linkage:

extern "C" void foo();

and you attempt to use it in a C++ program with C++ linkage.

If an entire library is included in a header file (and was compiled as C code); the include will need to be as follows;

extern "C" {
    #include "cheader.h"
}
balp4ylt

balp4ylt26#

what is an "undefined reference/unresolved external symbol"

I'll try to explain what is an "undefined reference/unresolved external symbol".
note: i use g++ and Linux and all examples is for it

For example we have some code

// 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);
}

Make object files

$ g++ -c src1.cpp -o src1.o
$ g++ -c src2.cpp -o src2.o

After the assembler phase we have an object file, which contains any symbols to export. Look at the symbols

$ 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]

I've rejected some lines from output, because they do not matter

So, we see follow symbols to export.

[1] - this is our static (local) variable (important - Bind has a type "LOCAL")
[2] - this is our global variable

src2.cpp exports nothing and we have seen no its symbols

Link our object files

$ g++ src1.o src2.o -o prog

and run it

$ ./prog
123

Linker sees exported symbols and links it. Now we try to uncomment lines in src2.cpp like here

// 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);
}

and rebuild an object file

$ g++ -c src2.cpp -o src2.o

OK (no errors), because we only build object file, linking is not done yet. Try to link

$ 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

It has happened because our local_var_name is static, i.e. it is not visible for other modules. Now more deeply. Get the translation phase output

$ 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

So, we've seen there is no label for local_var_name, that's why linker hasn't found it. But we are hackers :) and we can fix it. Open src1.s in your text editor and change

.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

i.e. you should have like below

.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:
; ...

we have changed the visibility of local_var_name and set its value to 456789. Try to build an object file from it

$ g++ -c src1.s -o src2.o

ok, see readelf output (symbols)

$ readelf --symbols src1.o
8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name

now local_var_name has Bind GLOBAL (was LOCAL)

link

$ g++ src1.o src2.o -o prog

and run it

$ ./prog 
123456789

ok, we hack it :)

So, as a result - an "undefined reference/unresolved external symbol error" happens when the linker cannot find global symbols in the object files.

o7jaxewo

o7jaxewo27#

The order in which interdependent linked libraries are specified is wrong.

The order in which libraries are linked DOES matter if the libraries depend on each other. In general, if library A depends on library B, then libAMUSTappear before libB in the linker flags.

For example:

// B.h

# ifndef B_H

# define B_H

struct B {
    B(int);
    int x;
};

# endif

// B.cpp

# include "B.h"

B::B(int xx) : x(xx) {}

// A.h

# include "B.h"

struct A {
    A(int x);
    B b;
};

// A.cpp

# include "A.h"

A::A(int x) : b(x) {}

// main.cpp

# include "A.h"

int main() {
    A a(5);
    return 0;
};

Create the libraries:

$ g++ -c A.cpp
$ g++ -c B.cpp
$ ar rvs libA.a A.o 
ar: creating libA.a
a - A.o
$ ar rvs libB.a B.o 
ar: creating libB.a
a - B.o

Compile:

$ g++ main.cpp -L. -lB -lA
./libA.a(A.o): In function `A::A(int)':
A.cpp:(.text+0x1c): undefined reference to `B::B(int)'
collect2: error: ld returned 1 exit status
$ g++ main.cpp -L. -lA -lB
$ ./a.out

So to repeat again, the orderDOESmatter!

rseugnpd

rseugnpd28#

Declared but did not define a variable or function.

A typical variable declaration is

extern int x;

As this is only a declaration, asingle definitionis 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:

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.

wvt8vs2t

wvt8vs2t29#

链接对应的库/对象文件或编译实现文件失败

通常,每个翻译单元将生成一个目标文件,其中包含在该翻译单元中定义的符号的定义。要使用这些符号,您必须链接到这些目标文件。

GCC下,您可以指定要在命令行中链接在一起的所有目标文件,或者一起编译实现文件。

g++ -o test objectFile1.o objectFile2.o -lLibraryName

-l...必须位于任何.o/.c/.cpp文件的右侧。

这里的libraryName只是库的简单名称,没有特定于平台的附加内容。例如,在Linux上,库文件通常称为libfoo.so,但您只需编写-lfoo。在Windows上,相同的文件可能被称为foo.lib,但您将使用相同的参数。您可能需要使用-L‹directory›添加可以找到这些文件的目录。确保在-l-L之后不写入空格。

对于XCode:添加用户头搜索路径->添加库搜索路径->将实际的库引用拖放到项目文件夹中。

MSVS下,添加到工程中的文件会自动将其目标文件链接在一起,并生成一个lib文件(常用)。要在单独的项目中使用符号,需要在项目设置中包括lib文件。这是在Input -> Additional Dependencies中的项目属性的Linker部分中完成的。(应该在Linker -> General -> Additional Library Directories中添加lib文件的路径)使用随lib文件提供的第三方库时,如果不这样做,通常会导致错误。

还可能发生忘记将文件添加到编译中的情况,在这种情况下,不会生成目标文件。在GCC中,将文件添加到命令行。在MSVS中,将文件添加到项目中将使其自动编译(尽管文件可以手动从构建中单独排除)。

在Windows编程中,未链接所需库的标志是未解析符号的名称以__imp_开头。在文档中查找函数的名称,它应该说明您需要使用哪个库。例如,MSDN将信息放在每个函数底部名为“库”的部分中的一个框中。

ux6nzvsh

ux6nzvsh30#

类成员:

virtual析构函数需要实现。

将析构函数声明为纯函数仍然需要您定义它(与常规函数不同):

struct X
{
    virtual ~X() = 0;
};
struct Y : X
{
    ~Y() {}
};
int main()
{
    Y y;
}
//X::~X(){} //uncomment this line for successful definition

这是因为在隐式销毁对象时会调用基类析构函数,因此需要定义。

virtual方法必须实现或定义为纯方法。

这类似于没有定义的非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
}

为此,请将X::foo()声明为纯:

struct X
{
    virtual void foo() = 0;
};

virtual类成员

有些成员即使没有显式使用,也需要定义:

struct A
{ 
    ~A();
};

以下内容将产生错误:

A a;      //destructor undefined

在类定义本身中,实现可以内联:

struct A
{ 
    ~A() {}
};

或室外:

A::~A() {}

如果实现在类定义之外,但在头中,则必须将方法标记为inline,以防止多重定义。

如果使用,则需要定义所有使用的成员方法。

一个常见的错误是忘记对名称进行限定:

struct A
{
   void foo();
};

void foo() {}

int main()
{
   A a;
   a.foo();
}

其定义应为

void A::foo() {}

static数据成员必须在类外定义单个翻译单元

struct X
{
    static int x;
};
int main()
{
    int x = X::x;
}
//int X::x; //uncomment this line to define X::x

可以为类定义中整型或枚举型的static``const数据成员提供初始值设定项;然而,ODR使用该成员仍然需要如上所述的命名空间范围定义。C++11允许在类内对所有static const数据成员进行初始化。

相关问题