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

什么是未定义的引用/未解析的外部符号错误? 什么是常见原因以及如何修复/防止它们?

随意编辑/添加自己的。


编译一个C ++程序需要几个步骤,如2.2所指出的(授予Keith Thompson作为参考):

翻译的语法规则中的优先顺序由以下几个阶段规定[见脚注]。

  • 必要时,以实现定义的方式将物理源文件字符映射到基本源字符集(为行尾指示符引入新行字符)。 [SNIP]
  • 删除每个反斜线字符()后面紧跟着一个换行符的字符,拼接物理源代码行以形成逻辑源代码行。 [SNIP]
  • 源文件被分解为预处理令牌(2.5)和空白字符序列(包括注释)。 [SNIP]
  • 执行预处理指令,展开宏调用,并执行_Pragma一元运算符表达式。 [SNIP]
  • 字符文字或字符串文字中的每个源字符集成员,以及字符文字或非原始字符串文字中的每个转义序列和通用字符名称都被转换为执行字符集的相应成员; [SNIP]
  • 相邻的字符串文字标记被连接在一起。
  • 分隔令牌的空白字符不再重要。 每个预处理令牌都被转换为令牌。 (2.7)。 所产生的令牌在语法和语义上被分析并翻译为翻译单元。 [SNIP]
  • 翻译的翻译单元和实例化单元组合如下:[SNIP]
  • 所有外部实体引用都已解析。 链接库组件以满足对当前翻译中未定义的实体的外部引用。 所有这些翻译器输出都被收集到一个程序映像中,其中包含执行环境中执行所需的信息。 (强调我的)
  • [脚注]实现必须表现得好像这些分离的阶段一样,尽管在实践中不同的阶段可能会被折叠在一起。

    指定的错误发生在编译的最后阶段,通常称为链接。 它基本上意味着你编译了一堆实现文件到目标文件或库中,现在你想让它们一起工作。

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

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

    其他编译器/平台也有类似的机制。

    常见错误消息是error LNK2001 error LNK1120 error LNK2019Microsoft Visual Studio error LNK2019undefined reference toGCC symbolName undefined reference to

    代码:

    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
    

    常见原因包括:

  • 未能链接到适当的库/对象文件或编译实现文件
  • 声明和未定义的变量或函数。
  • 类类成员的常见问题
  • 模板实现不可见。
  • 符号在C程序中定义并用于C ++代码。
  • 在modules / dll中错误地导入/导出方法/类。 (特定于MSVS)
  • 循环库依赖
  • 未定义的引用'WinMain @ 16'
  • 相互依存的图书馆秩序
  • 多个相同名称的源文件
  • 使用#pragma (Microsoft Visual Studio)时不要使用.lib扩展名
  • 模板朋友的问题
  • UNICODE定义不一致

  • 班级成员:

    一个纯粹的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数据成员进行初始化。


    未能链接到适当的库/对象文件或编译实现文件

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

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

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

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

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

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

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

    在Windows编程中,未链接必要库的迹象表明,未解析符号的名称以__imp_ 。 在文档中查找该函数的名称,并且应该说明您需要使用哪个库。 例如,MSDN将信息放在每个函数底部的一个框中,称为“库”。

    链接地址: http://www.djcxy.com/p/43871.html

    上一篇: What is an undefined reference/unresolved external symbol error and how do I fix it?

    下一篇: gprof output is empty