undefined reference to 问题详解

错误解释

调用了没有实现的函数。链接阶段链接器会将编译c/cpp生成的.o/.obj文件链接生成.exe/.so/.dll/.a/.lib等格式的可执行程序或库文件,c/cpp代码之间函数的相互调用,会在这个阶段由链接器将函数具体实现的地址写入可执行程序或库文件。当链接器找不到某个函数的具体实现时,就会抛出undefined reference to错误。

  • 发生阶段:链接
  • 错误级别:error

常见错误举例

  1. 调用extern声明的函数,未找到该函数的实现

    extern void TestUndefinedReferenceTo();
    int main()
    {
       TestUndefinedReferenceTo();
       return 0;
    }

    正确写法

    extern void TestUndefinedReferenceTo()
    {
       cout << "TestUndefinedReferenceTo" << endl;
    }
    int main()
    {
       TestUndefinedReferenceTo();
       return 0;
    }
  2. 在.h中声明的类成员函数,没有实现

    class Test {
    public:
       void TestUndefinedReferenceTo();
    };
    int main()
    {
       Test test;
       test.TestUndefinedReferenceTo();
       return 0;
    }

    正确写法

    class Test {
    public:
       void TestUndefinedReferenceTo();
    };
    void Test::TestUndefinedReferenceTo()
    {
       cout << "TestUndefinedReferenceTo" << endl;
    }
    int main()
    {
       Test test;
       test.TestUndefinedReferenceTo();
       return 0;
    }
  3. 代码没有问题,构建脚本少链接了对应的实现

    例如使用了winsock2.h中的网络字节序转换的接口ntohl,但是没有链接Ws2_32库。

    #include 
    #include 
    using namespace std;
    int main()
    {
       cout << ntohl(0x1245) << endl;
       return 0;
    }

    正确写法

    在构建脚本中链接选项中加上-lWs2_32,以cmake为例:

    target_link_libraries(myTarget PUBLIC -lWs2_32)
  4. 依赖的库文件中函数定义已发生变化,没有及时更新头文件

    新版本头文件中的声明

    class Test {
    public:
      void TestUndefinedReferenceTo(const std::string name);
    };

    新版本lib.so中的实现

    void Test::TestUndefinedReferenceTo(const string name)
    {
       cout << "TestUndefinedReferenceTo:" << name << endl;
    }

    旧版本头文件中的声明

    class Test {
    public:
       void TestUndefinedReferenceTo(std::string name);
    };

    如果test.cpp使用了新版本的lib.so和旧版本的头文件,test.cpp编译时调用了不带const的接口,而链接时只有带const接口的实现,找不到不到const的实现,将会抛出undefined reference to 错误。

    正确写法

    链接时使用配套的头文件和库文件

通用分析解决流程

  1. 通过链接错误日志,确认在链接哪个库/可执行程序时报的错,找不到的符号是什么,在哪里调用了这个符号

    例如:

    g++.exe -g  CMakeFiles/testCompileError.dir/main.cpp.obj -o testCompileError.exe ..."
    CMakeFiles/testCompileError.dir/main.cpp.obj: In function `main':
    E:/mywork/testCompileError/main.cpp:8: undefined reference to `__imp_ntohl'
    collect2.exe: error: ld returned 1 exit status

    链接哪个库/可执行程序时报的错:testCompileError.exe

    找不到的符号是什么:__imp_ntohl

    在哪里调用了这个符号:main.cpp:8

  2. 根据找不到的符号名,确认该函数是三方库调用,还是要在本地代码中实现。如果是三方库调用,则确认库的名字与路径;如果是本地代码实现,搜索代码检查改实现是否存在。

    当无法确认时可以先以文本方式全量搜索代码,再全量搜索库文件中的符号表。符号打印在linux下可以使用nm、readelf等工具,在windows下可以使用dumpbin工具,这里以linux为例,全量搜索当前目录下所有库文件的符号表

    tiny% find ./ -regex  ".*\.a\|.*\.so" -exec nm -CA {} \; | grep -H __imp_ntohl
    (standard input):./libws2_32.a:deyfs00186.o:0000000000000000 I __imp_ntohl
    (standard input):./libwsock32.a:dsxbs00060.o:0000000000000000 I __imp_ntohl
  3. 检查构建脚本,确认三方库/本地实现是否参与链接。