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


当前回答

编译器/IDE中的错误

我最近遇到了这个问题,结果发现这是Visual Studio Express 2013中的一个错误。我不得不从项目中删除一个源文件,然后重新添加它以克服错误。

如果您认为这可能是编译器/IDE中的错误,请尝试以下步骤:

清理项目(一些IDE可以选择这样做,您也可以手动删除对象文件)尝试启动新项目,从原始代码复制所有源代码。

其他回答

使用带有代码运行程序扩展名和多个.c或.cpp文件的Visual Studio代码

所提供的Code Runner仅适用于具有单个源文件的编译程序。它不是为与多个源文件一起使用而设计的。您应该使用不同的扩展名,例如C/C++Makefile Project扩展名或CMake Tools扩展名,或者修复CodeRunner扩展名以处理多个文件,或者手动编辑.json配置文件。

不同的架构

您可能会看到这样的消息:

library machine type 'x64' conflicts with target machine type 'X86'

在这种情况下,这意味着可用符号用于不同于您正在编译的体系结构。

在Visual Studio上,这是由于错误的“平台”,您需要选择正确的平台或安装正确版本的库。

在Linux上,这可能是由于错误的库文件夹(例如,使用lib而不是lib64)。

在MacOS上,可以选择在同一文件中传送两种体系结构。可能是链接希望两个版本都存在,但只有一个版本存在。也可能是库所在的lib/lib64文件夹错误。

编译器/IDE中的错误

我最近遇到了这个问题,结果发现这是Visual Studio Express 2013中的一个错误。我不得不从项目中删除一个源文件,然后重新添加它以克服错误。

如果您认为这可能是编译器/IDE中的错误,请尝试以下步骤:

清理项目(一些IDE可以选择这样做,您也可以手动删除对象文件)尝试启动新项目,从原始代码复制所有源代码。

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

MSVS要求您使用__declspec(dllexport)和__declsspec(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
{
};

函数或类方法在源文件中使用内联说明符定义。

例如:-

主.cpp

#include "gum.h"
#include "foo.h"

int main()
{
    gum();
    foo f;
    f.bar();
    return 0;
}

foo.h(1)

#pragma once

struct foo {
    void bar() const;
};

口香糖.h(1)

#pragma once

extern void gum();

foo.cpp(1)

#include "foo.h"
#include <iostream>

inline /* <- wrong! */ void foo::bar() const {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

gum.cpp(1)

#include "gum.h"
#include <iostream>

inline /* <- wrong! */ void gum()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

如果指定gum(类似地,foo::bar)在其定义中是内联的,那么编译器将通过以下方式内联gum(如果它选择):-

没有任何独特的口香糖定义,因此不发出任何符号,链接器可以通过该符号引用口香糖的定义,而是将所有对gum的调用替换为编译后的gum主体的内联副本。

因此,如果在源文件gum.cpp中内联定义gum,则编译为对象文件gum.o,其中所有对gum的调用都是内联的并且没有定义接头可以指代口香糖的符号。当你将gum.o与另一个对象文件(例如main.o)链接到程序中引用外部符号gum时,链接器无法解析这些参考文献。因此连杆失效:

编译:

g++ -c  main.cpp foo.cpp gum.cpp

链接:

$ g++ -o prog main.o foo.o gum.o
main.o: In function `main':
main.cpp:(.text+0x18): undefined reference to `gum()'
main.cpp:(.text+0x24): undefined reference to `foo::bar() const'
collect2: error: ld returned 1 exit status

如果编译器可以在调用gum的每个源文件中看到它的定义,则只能将gum定义为内联。这意味着它的内联定义需要存在于包含在每个源文件中的头文件中您可以在其中调用gum。做两件事之一:

要么不内联定义

从源文件定义中删除内联说明符:

foo.cpp(2)

#include "foo.h"
#include <iostream>

void foo::bar() const {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

gum.cpp(2)

#include "gum.h"
#include <iostream>

void gum()
{
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

重新生成:

$ g++ -c  main.cpp foo.cpp gum.cpp
imk@imk-Inspiron-7559:~/develop/so/scrap1$ g++ -o prog main.o foo.o gum.o
imk@imk-Inspiron-7559:~/develop/so/scrap1$ ./prog
void gum()
void foo::bar() const

成功

或正确内联

头文件中的内联定义:

foo.h(2)

#pragma once
#include <iostream>

struct foo {
    void bar() const  { // In-class definition is implicitly inline
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
};
// Alternatively...
#if 0
struct foo {
    void bar() const;
};
inline void foo::bar() const  {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}
#endif

口香糖.h(2)

#pragma once
#include <iostream>

inline void gum() {
    std::cout << __PRETTY_FUNCTION__ << std::endl;
}

现在我们不需要foo.cpp或gum.cpp:

$ g++ -c main.cpp
$ g++ -o prog main.o
$ ./prog
void gum()
void foo::bar() const