Cxxdroid 中文文档 下载 App

链接错误 undefined reference —— 库没链接还是符号不存在

是什么意思

编译(compile)和链接(link)是两个阶段:

main.cpp → [编译] → main.o(目标文件)
utils.cpp → [编译] → utils.o
                              ↓
         [链接器] → mygame(可执行文件)
                ↑
        库:libSDL2.a、libSDL2_image.a...

编译器只负责检查语法和类型,链接器负责把所有目标文件和库拼起来。undefined reference to XXX 的意思是:

目标文件里调用了 XXX 这个函数/变量,但链接器在所有提供的库和目标文件里找不到它的定义。

排查顺序(从最可能到最不可能)

第一步:是不是漏了某个库

90% 的 undefined reference 都是这个原因。

比如你在代码里用了 SDL_Init,但 CMake 里忘了链接 SDL2:

int main() {
    SDL_Init(SDL_INIT_VIDEO);  // undefined reference to SDL_Init
    return 0;
}

CMakeLists.txt 里应该有:

find_package(SDL2 REQUIRED)
target_link_libraries(mygame PRIVATE SDL2::SDL2)

缺失的典型库:

undefined reference缺失的库
SDL_*SDL2
IMG_*, TTF_*, Mix_*SDL_image / SDL_ttf / SDL_mixer
boost::*Boost 对应组件
sqlite3_*SQLite3
curl_*CURL
std::filesystem(旧 Clang)c++fs(C++17 前需要)

第二步:链接顺序对不对

有些链接器对链接顺序敏感——依赖库必须在被依赖库的后面。

# 可能有问题:SDL2_image 在 SDL2 前面
target_link_libraries(mygame PRIVATE SDL2_image SDL2)

# 正确:被依赖的在后面
target_link_libraries(mygame PRIVATE SDL2 SDL2_image)

SDL_image 依赖 SDL2,所以 SDL2 必须在 SDL_image 的后面。同理:

第三步:是不是只声明了没定义

有些情况不是漏库,而是代码本身的问题:

class Singleton {
    static Singleton instance;  // 只声明
};
// 没在任何 .cpp 文件里定义 Singleton::instance → undefined reference

或者:

void greet();  // 函数声明
// 没在任何地方实现 void greet() {} → undefined reference to greet

检查一下:声明的函数、静态变量有没有对应的定义。

第四步:库架构不匹配

极少见,但可能发生:

Cxxdroid 包管理器装的库默认和编译器架构一致(arm64),所以这步一般没问题。如果你手动拷了库进来,注意检查架构。

第五步:版本不兼容

库版本升级后可能改变了 API:

比如 SDL2.0.18 之后某些 SDL_CreateWindow 的行为变了,旧代码可能编译不过。

快速验证:用库自带的最小示例代码编译一下,如果示例能编译通过,说明库本身没问题,是你代码的问题。

工具:链接器 verbose 输出

如果手动在 CMake 里加:

target_link_options(mygame PRIVATE -Wl,--verbose)

链接器会详细输出它尝试链接了哪些库、哪些符号在哪找到的。输出很长,但对于极其诡异的 undefined reference 有用。

常见 undefined reference 速查表

undefined reference快速修复
SDL_Init@ / SDL_Init@@加 target_link_libraries(... SDL2::SDL2)
IMG_Load@加 SDL2_image
curl_easy_init@加 CURL
sqlite3_open@加 SQLite3
boost::system::...加 Boost::system
boost::filesystem::...加 Boost::filesystem
std::filesystem::...C++17 下不需要,旧编译器需要加 stdc++fs
pthread_create@加线程库(通常自动链接,有些情况需要手动加 pthread)
operator new@通常是 C++ 标准库问题,确认 CMAKE_CXX_STANDARD_LIBRARIES 正确