看到红字别慌——先分清是编译错误还是链接错误,再对号入座
新手最容易犯的错,是把链接错误当成语法错误去逐行改代码——方向就错了。 其实两者一眼能分:
| 特征 | 编译期错误 | 链接期错误 |
|---|---|---|
| 报错里有没有 | 只有 error: 行 | 有 ld.exe / collect2.exe |
| 指不指具体行列 | 指(如 e6.cpp:1:13) | 指 .o 文件与符号,不指你的源码行 |
| 典型关键词 | was not declared、expected、cannot convert | undefined reference、multiple definition、cannot find -l |
| 该往哪改 | 报错指的那一行源码 | 缺的定义在哪、库链没链上——不在报错那行 |
ld.exe、collect2、undefined reference、multiple definition
→ 链接期,别改报错那一行;只有 error: 且带 文件:行:列 → 编译期,就改那一行。把你遇到的报错关键词粘进搜索框(如 undefined reference、vtable、bad_alloc),
或点上面的阶段标签筛选。每条都是报错原文 → 真实原因 → 修法三栏。
undefined reference 不是"这行代码写错了",
而是"这个符号的定义没链接进来"。盯着报错那行改半天也没用,要去找定义。expected ';' before X,要回头查 X 的上一行末尾。error:,改完再编,后面的常常自己就消失了。std::bad_alloc 是编译错误(E13):它编译能过,是运行时
new 要不到内存才抛的。要在运行时 catch,不是去改编译选项。undefined reference to 'foo()' 且前面有 ld.exe,应该?ld.exe + undefined reference = 链接期错误(E1/E3/E5)。链接器找不到 foo() 的定义,跟"哪一行调用了它"无关。要检查:定义写了吗?定义所在的 .cpp 参与编译/链接了吗?声明和定义的签名一致吗?multiple definition of 'g_count' 最常见的根因是?int g_count = 0; 是定义,每个 include 它的 .cpp 都会得到一份,链接时就"重复定义"。修法:头文件改成 extern int g_count;(只声明),真正的定义只放进一个 .cpp。missing separator,通常是因为?missing separator。这是 Makefile 最经典的坑——很多编辑器的"自动缩进"会偷偷把 TAB 变成空格。Q4. 你写了个模板 template<class T> T max2(T a,T b); 声明在 util.h,
实现放在 util.cpp,另一个文件 include 后调用 max2(3,7),链接时报
undefined reference to 'int max2<int>(int,int)'。为什么?怎么修?
max2(3,7))
且同时能看到模板定义时,才能生成 max2<int> 的代码。定义藏在 util.cpp 里,
调用方 include 的头文件只有声明,于是链接期找不到实例。util.h)。这是模板的铁律,也是"模板为什么都写在头文件"的原因(对应 30.4)。
Q5. 链接报 undefined reference to `vtable for Base',
但你觉得 Base 的函数都写了。可能漏了什么?
vtable(虚表)由编译器生成,生成的触发点是类里第一个非内联、非纯虚的虚函数的定义。
如果你声明了 virtual void f(); 却忘了写它的实现,编译器就没地方挂虚表,链接期报 undefined reference to vtable。virtual void f() = 0;(那样就该实例化派生类而非基类)。
Q6. 一个跨平台工程,本地能编译链接通过,同事用某个预编译第三方库时却报
undefined reference to 'get_name[abi:cxx11]()'。报错里那个 [abi:cxx11] 说明了什么?怎么定位?
[abi:cxx11] 表示调用方在找 C++11 新 ABI 版本的
std::string 符号(std::__cxx11::basic_string),但预编译库里导出的是旧 ABI
(_GLIBCXX_USE_CXX11_ABI=0)的符号——两者符号名不同,链接器自然找不到。[abi:cxx11] 或 __cxx11 字样;
② 用 nm -C 库.a | grep 符号名 看库导出的到底是哪个 ABI 版本;
③ 修法:让主程序和第三方库用同一套 _GLIBCXX_USE_CXX11_ABI 设置重新编译,
或换用与你的标准库 ABI 匹配的预编译版本(对应 40.1 依赖与 ABI 兼容)。