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

C++ 什么是未定义的引用/未解决的外部符号错误?如何修复它?,c++,linker-errors,undefined-reference,c++-faq,unresolved-external,visual-studio,C++,Linker Errors,Undefined Reference,C++ Faq,Unresolved External,Visual Studio,什么是未定义的引用/未解决的外部符号错误?什么是常见的原因和如何修复/阻止它们?< /P> < P>编译C++程序发生在几个步骤中,如 2.2 < /强>:< /P> 翻译语法规则的优先顺序由以下阶段规定[见脚注] 物理源文件字符以实现定义的方式映射到基本源字符集 (为行尾指示器引入新行字符)如果 必要的。[剪报] 删除紧跟在新行字符后面的反斜杠字符(\)的每个实例,将物理源行拼接到 形成逻辑源行。[剪报] 源文件被分解为预处理标记(2.5)和空格字符序列(包括注释)。[剪报] 执行预处理指令,

什么是未定义的引用/未解决的外部符号错误?什么是常见的原因和如何修复/阻止它们?< /P> < P>编译C++程序发生在几个步骤中,如<强> 2.2 < /强>:< /P> 翻译语法规则的优先顺序由以下阶段规定[见脚注]

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

    g++ -o test objectFile1.o objectFile2.o -lLibraryName
    
    指定的错误发生在编译的最后阶段,通常称为链接。这基本上意味着您将一组实现文件编译成了目标文件或库,现在您想让它们一起工作

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

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

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

    常见的错误消息有
    错误LNK2001
    错误LNK1120
    错误LNK2019
    对于Microsoft Visual Studio
    对于GCC
    符号名的引用未定义

    守则:

    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
    
    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
    
    以及Microsoft Visual Studio的类似错误:

    /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
    
    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
    
    常见原因包括:

    未能链接到适当的库/对象文件或编译实现文件 通常,每个翻译单元将生成一个对象文件,其中包含在该翻译单元中定义的符号的定义。 要使用这些符号,必须针对这些对象文件进行链接

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

    g++ -o test objectFile1.o objectFile2.o -lLibraryName
    
    这里的
    libraryName
    只是库的简单名称,没有特定于平台的添加。例如,在Linux上,库文件通常称为
    libfoo.So
    ,但您只需编写
    -lfoo
    。在Windows上,同一个文件可能被称为
    foo.lib
    ,但使用相同的参数。您可能必须使用
    -Lèdirectory›
    添加可在其中找到这些文件的目录。确保不要在
    -l
    -l
    后写空格

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

    MSVS下,添加到项目中的文件会自动将其对象文件链接在一起,并生成
    lib
    文件(常用)。要在单独的项目中使用这些符号,您需要 需要在项目设置中包含
    lib
    文件。这是在项目属性的链接器部分,在
    Input->additionaldependencies
    中完成的。(指向
    lib
    文件的路径应为 添加到
    Linker->General->Additional Library Directories
    )当使用随
    lib
    文件提供的第三方库时,否则通常会导致错误

    您还可能忘记将文件添加到编译中,在这种情况下,将不会生成目标文件。在gcc中,将文件添加到命令行。在MSVS中,将文件添加到项目中将使其自动编译(尽管文件可以,但是
    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)
                              
    
    struct X
    {
        virtual ~X() = 0;
    };
    struct Y : X
    {
        ~Y() {}
    };
    int main()
    {
        Y y;
    }
    //X::~X(){} //uncomment this line for successful definition
    
    struct X
    {
        virtual void foo();
    };
    struct Y : X
    {
       void foo() {}
    };
    int main()
    {
       Y y; //linker error although there was no call to X::foo
    }
    
    struct X
    {
        virtual void foo() = 0;
    };
    
    struct A
    { 
        ~A();
    };
    
    A a;      //destructor undefined
    
    struct A
    { 
        ~A() {}
    };
    
    A::~A() {}
    
    struct A
    {
       void foo();
    };
    
    void foo() {}
    
    int main()
    {
       A a;
       a.foo();
    }
    
    void A::foo() {}
    
    struct X
    {
        static int x;
    };
    int main()
    {
        int x = X::x;
    }
    //int X::x; //uncomment this line to define X::x
    
    template<class T>
    struct X
    {
        void foo();
    };
    
    int main()
    {
        X<int> x;
        x.foo();
    }
    
    //differentImplementationFile.cpp
    template<class T>
    void X<T>::foo()
    {
    }
    
    void foo();
    int main()
    {
        foo();
    }
    
    extern "C" void foo();
    int main()
    {
        foo();
    }
    
    extern "C" void foo();
    
    extern "C" {
        #include "cheader.h"
    }
    
    #ifdef THIS_MODULE
    #define DLLIMPEXP __declspec(dllexport)
    #else
    #define DLLIMPEXP __declspec(dllimport)
    #endif
    
    DLLIMPEXP void foo();
    
    __declspec(dllexport) void foo();
    
    __declspec(dllimport) void foo();
    
    class DLLIMPEXP X
    {
    };
    
    // 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;
    };
    
    $ 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
    
    $ 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
    
    #pragma comment(lib, "libname.lib")
    
    // 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;
    }
    
    // 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
    
    $ g++ src1.o src2.o -o prog
    
    $ ./prog
    123
    
    // 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
    
    $ 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  _ZL14local_var_name
    .comm   _ZL14local_var_name,4,4
    
        .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:
    ; ...
    
    $ g++ -c src1.s -o src2.o
    
    $ readelf --symbols src1.o
    8: 0000000000000000     4 OBJECT  GLOBAL DEFAULT    3 local_var_name
    
    $ g++ src1.o src2.o -o prog
    
    $ ./prog 
    123456789
    
    INPUT (libtbb.so.2)
    
    cp libtbb.so.2 libtbb.so
    
    int foo()
    {
        return 0;
    }
    
    void foo();
    
    void bar()
    {
        foo();
    }
    
    template <typename T>
    class Foo {
        friend std::ostream& operator<< (std::ostream& os, const Foo<T>& a);
    };
    
    std::ostream& operator<< (std::ostream& os, const Foo<int>& a) {/*...*/}
    
    // 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
    }
    
    template <typename T>
    class Foo {
        template <typename T1>
        friend std::ostream& operator<<(std::ostream& os, const Foo<T1>& a);
        // ...
    };
    
    template <typename T>
    class Foo {
        friend std::ostream& operator<<(std::ostream& os, const Foo& a)
        { /*...*/ }
        // ...
    };
    
    #define UNICODE
    #define _UNICODE
    
    /DUNICODE /D_UNICODE
    
    // header1.h
    typedef int Number;
    void foo(Number);
    
    // header2.h
    typedef float Number;
    void foo(Number); // this only looks the same lexically
    
    // graphics.lib    
    #include "common_math.h" 
       
    void draw(vec3 p) { ... } // vec3 comes from common_math.h
    
    // main.exe
    #include "other/common_math.h"
    #include "graphics.h"
    
    int main() {
        draw(...);
    }
    
    #include "my_lib.h"
    #include <stdio.h>
    
    void hw(void)
    {
        puts("Hello World");
    }
    
    #ifndef MY_LIB_H
    #define MT_LIB_H
    
    extern void hw(void);
    
    #endif
    
    #include <my_lib.h>
    
    int main()
    {
        hw();
        return 0;
    }
    
    $ gcc -c -o my_lib.o my_lib.c
    $ ar rcs libmy_lib.a my_lib.o
    
    $ gcc -I. -c -o eg1.o eg1.c
    
    $ gcc -o eg1 -L. -lmy_lib eg1.o 
    eg1.o: In function `main':
    eg1.c:(.text+0x5): undefined reference to `hw'
    collect2: error: ld returned 1 exit status
    
    $ gcc -o eg1 -I. -L. -lmy_lib eg1.c
    /tmp/ccQk1tvs.o: In function `main':
    eg1.c:(.text+0x5): undefined reference to `hw'
    collect2: error: ld returned 1 exit status
    
    #include <zlib.h>
    #include <stdio.h>
    
    int main()
    {
        printf("%s\n",zlibVersion());
        return 0;
    }
    
    $ gcc -c -o eg2.o eg2.c
    
    $ gcc -o eg2 -lz eg2.o 
    eg2.o: In function `main':
    eg2.c:(.text+0x5): undefined reference to `zlibVersion'
    collect2: error: ld returned 1 exit status
    
    $ gcc -o eg2 -I. -lz eg2.c
    /tmp/ccxCiGn7.o: In function `main':
    eg2.c:(.text+0x5): undefined reference to `zlibVersion'
    collect2: error: ld returned 1 exit status
    
    $ gcc -o eg2 $(pkg-config --libs zlib) eg2.o 
    eg2.o: In function `main':
    eg2.c:(.text+0x5): undefined reference to `zlibVersion'
    
    $ gcc -o eg1 eg1.o -L. -lmy_lib
    
    $ ./eg1 
    Hello World
    
    $ gcc -o eg2 eg2.o -lz
    
    $ ./eg2 
    1.2.8
    
    $ gcc -o eg2 eg2.o $(pkg-config --libs zlib) 
    $ ./eg2
    1.2.8
    
    $ gcc -o eg1 -L. -lmy_lib eg1.o
    
    gcc -o eg2 -lz eg2.o
    
    gcc -o eg2 $(pkg-config --libs zlib) eg2.o
    
    gcc -o eg2 -lz eg2.o
    
    gcc -o eg2 -lz eg2.o
    
    $ gcc -o eg1 -I. -L. -lmy_lib eg1.c
    
    $ gcc -I. -c -o eg1.o eg1.c
    $ gcc -o eg1 -L. -lmy_lib eg1.o
    
    /tmp/ccQk1tvs.o: In function `main'
    
    eg1.o: In function `main':
    
    // file1.cpp
    const int test = 5;    // in C++ same as "static const int test = 5"
    int test2 = 5;
    
    // file2.cpp
    extern const int test;
    extern int test2;
    
    void foo()
    {
     int x = test;   // linker error in C++ , no error in C
     int y = test2;  // no problem
    }
    
    extern const int test;
    extern int test2;
    
    #if (defined _GLIBCXX_EXPERIMENTAL_FILESYSTEM) //is the included filesystem library experimental? (C++14 and newer: <experimental/filesystem>)
    using path_t = std::experimental::filesystem::path;
    #elif (defined _GLIBCXX_FILESYSTEM) //not experimental (C++17 and newer: <filesystem>)
    using path_t = std::filesystem::path;
    #endif
    
    # -D shows (global) dynamic symbols that can be used from the outside of XXX.so
    nm -D XXX.so | grep MY_SYMBOL 
    
    nm XXX.so
    00000000000005a7 t HIDDEN_SYMBOL
    00000000000005f8 T VISIBLE_SYMBOL
    
    #define DLL_PUBLIC __attribute__ ((visibility ("default")))
    
    DLL_PUBLIC int my_public_function(){
      ...
    }
    
    #ifdef BUILDING_DLL
        #define DLL_PUBLIC __declspec(dllexport) 
    #else
        #define DLL_PUBLIC __declspec(dllimport) 
    #endif
    
    >>> objdump -t XXXX.o | grep hidden
    0000000000000000 g     F .text  000000000000000b .hidden HIDDEN_SYMBOL1
    000000000000000b g     F .text  000000000000000b .hidden HIDDEN_SYMBOL2
    
    #include "gum.h"
    #include "foo.h"
    
    int main()
    {
        gum();
        foo f;
        f.bar();
        return 0;
    }
    
    #pragma once
    
    struct foo {
        void bar() const;
    };
    
    #pragma once
    
    extern void gum();
    
    #include "foo.h"
    #include <iostream>
    
    inline /* <- wrong! */ void foo::bar() const {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    
    #include "gum.h"
    #include <iostream>
    
    inline /* <- wrong! */ void gum()
    {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    
    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
    
    #include "foo.h"
    #include <iostream>
    
    void foo::bar() const {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    
    #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
    
    #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
    
    #pragma once
    #include <iostream>
    
    inline void gum() {
        std::cout << __PRETTY_FUNCTION__ << std::endl;
    }
    
    $ g++ -c main.cpp
    $ g++ -o prog main.o
    $ ./prog
    void gum()
    void foo::bar() const