- gf25030 的博客
C++错误信息
- @ 2026-8-19 15:56:54
C++ 错误处理完全指南
一、错误分类概述
C++ 错误按阶段分为三大类:
| 阶段 | 错误类型 | 发现时机 | 典型表现 |
|---|---|---|---|
| 编译期 | 语法错误、类型错误、模板错误 | 编译时 | 编译器输出错误信息 |
| 链接期 | 未定义引用、重复定义 | 链接时 | undefined reference |
| 运行期 | 内存错误、异常、逻辑错误 | 程序运行时 | 崩溃、死循环、错误结果 |
核心原则:编译期错误是语法问题,链接期错误是组织问题,运行期错误是逻辑问题。
二、编译期错误详解
2.1 语法错误
编译器无法理解代码结构。
典型案例1:中英文标点混用
#include<iostream>
using namespace std; // 中文分号
int main() {
cout << "Hello"; // 中文分号
return 0;
}
报错信息:
error: stray '\243' in program
error: expected ';' before 'return'
解决方案:
- 始终使用英文输入法
- IDE 中开启"显示空白符"
- 使用 clang-format 格式化工具
典型案例2:缺少分号
int a = 10 // 缺少分号
int b = 20;
struct Node {
int val;
Node* next
} // 结构体定义后缺少分号
报错信息:
error: expected ';' before 'int'
error: expected ';' after struct definition
典型案例3:括号不匹配
if (a > 0 { // 缺少右括号
cout << a;
}
报错信息:
error: expected ')' before '{' token
2.2 类型错误
类型系统是 C++ 的核心特性,也是错误高发区。
典型案例1:类型不兼容
char c = "A"; // 双引号是字符串
string s = 'hello'; // 单引号是字符
int* p = 10; // 指针不能直接赋整数值
double d = 5 / 2; // 整数除法得 2,不是 2.5
报错信息:
error: invalid conversion from 'const char*' to 'char'
error: invalid conversion from 'int' to 'int*'
正确写法:
char c = 'A'; // 单引号
string s = "hello"; // 双引号
int* p = nullptr; // 空指针
double d = 5.0 / 2; // 浮点数除法
典型案例2:const 正确性
const int MAX = 100;
int* p = &MAX; // 丢弃 const 限定符
const char* str = "hello";
char* p = str; // 丢弃 const 限定符
报错信息:
error: invalid conversion from 'const int*' to 'int*'
2.3 声明与作用域错误
典型案例1:未声明的标识符
int main() {
cout << x << endl; // x 未声明
return 0;
}
报错信息:
error: 'x' was not declared in this scope
解决方案:
#include <iostream>
using namespace std;
int main() {
int x = 10;
cout << x << endl;
return 0;
}
典型案例2:重复定义
头文件问题:
// utils.h
int global_var = 100; // 在多个 .cpp 中包含会重复定义
正确做法:
// utils.h
extern int global_var; // 仅声明
// utils.cpp
int global_var = 100; // 在一个 cpp 中定义
解决方案:
- 使用
#pragma once或头文件守卫 - 全局变量使用
extern - 常量使用
constexpr替代#define
2.4 模板错误
模板错误信息通常很长,但核心问题往往简单。
典型案例1:模板嵌套的 >> 问题
priority_queue<int, vector<int>, greater<int>> q;
// C++98/03 中 >> 会被识别为右移运算符
报错信息:
error: '>>' should be '> >' within a nested template argument list
解决方案:
// C++03 需要加空格
priority_queue<int, vector<int>, greater<int> > q;
// C++11 及以上可直接使用
典型案例2:依赖名称需要 typename
template<typename T>
void func() {
T::iterator it; // iterator 是依赖名称
}
报错信息:
error: need 'typename' before 'T::iterator' because 'T' is a dependent scope
解决方案:
template<typename T>
void func() {
typename T::iterator it; // 加 typename
}
2.5 编译错误速查表
| 错误信息关键词 | 问题定位 | 快速检查 |
|---|---|---|
| stray | 非法字符 | 检查是否有中文标点 |
| expected ';' | 缺少分号 | 检查上一行末尾 |
| expected ')' | 括号不匹配 | 检查括号配对 |
| was not declared | 未声明 | 检查拼写、作用域、头文件 |
| invalid conversion | 类型不匹配 | 检查类型一致性 |
| no matching function | 函数重载不匹配 | 检查参数类型和个数 |
| redefinition | 重复定义 | 检查头文件守卫 |
三、链接错误
编译通过但链接失败,通常是多文件组织问题。
3.1 未定义引用
最常见也最容易让人困惑的错误。
// math.h
int add(int a, int b); // 声明
// math.cpp(忘记实现)
// int add(int a, int b) { return a + b; }
// main.cpp
#include "math.h"
int main() {
int result = add(3, 5); // 链接错误
return 0;
}
报错信息:
undefined reference to `add(int, int)'
collect2: error: ld returned 1 exit status
解决方案:
- 实现所有声明的函数
- 编译时包含所有源文件:
g++ main.cpp math.cpp -o program - 检查静态库顺序(依赖库放后面)
3.2 虚函数表错误
class Base {
public:
virtual void func(); // 声明虚函数
};
class Derived : public Base {
// 忘记实现 func
};
报错信息:
undefined reference to `vtable for Base'
undefined reference to `Base::func()'
解决方案:实现所有虚函数,纯虚函数加上 = 0。
四、运行时错误
4.1 内存错误
最危险的错误类型,可能导致安全漏洞。
段错误典型案例:
int* p = nullptr;
*p = 10; // 解引用空指针
int arr[5];
arr[10] = 100; // 数组越界
int* p = new int(10);
delete p;
*p = 20; // 使用已释放的内存
void recursive(int n) {
recursive(n + 1); // 无限递归导致栈溢出
}
排查工具:
# AddressSanitizer
g++ -fsanitize=address -g main.cpp
./a.out
# Valgrind
valgrind --leak-check=full ./a.out
# GDB
gdb ./a.out
run
bt # 查看调用栈
内存泄漏:
while (true) {
int* p = new int[1000]; // 永不释放
}
检测工具:Valgrind、AddressSanitizer、智能指针。
4.2 标准库异常
| 异常类型 | 触发场景 | 预防措施 |
|---|---|---|
| std::out_of_range | vector::at() 越界 | 检查索引范围 |
| std::bad_alloc | 内存分配失败 | 检查内存使用 |
| std::bad_cast | dynamic_cast 失败 | 检查类型转换 |
| std::invalid_argument | 参数无效 | 参数校验 |
| std::runtime_error | 通用运行时错误 | 查看错误信息 |
4.3 逻辑错误
编译运行都正常,但结果不对。
// 整数除法
double ratio = 1 / 2; // 得到 0.0
double ratio = 1.0 / 2; // 得到 0.5
// 赋值与比较混淆
if (a = 5) { /* 永远为真 */ } // 赋值
if (a == 5) { /* 正确比较 */ }
// 短路求值误用
if (ptr->data == 5 && ptr != nullptr) { } // 可能崩溃
if (ptr != nullptr && ptr->data == 5) { } // 正确
// 循环边界错误
for (int i = 0; i <= n; i++) { } // 多循环一次
for (int i = 0; i < n; i++) { } // 正确
五、调试工具链
5.1 编译器警告选项
# 开发阶段必须开启
g++ -Wall -Wextra -Werror -pedantic main.cpp
# 各选项含义
-Wall # 常用警告
-Wextra # 额外警告
-Werror # 警告视为错误
-pedantic # 严格 ISO C++ 标准
-g # 调试信息
-O0 # 关闭优化
5.2 调试工具对比
| 工具 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| GDB | 通用调试 | 功能强大 | 命令行学习曲线陡峭 |
| Visual Studio Debugger | Windows 开发 | 可视化 | 仅限 Windows |
| AddressSanitizer | 内存错误检测 | 速度快 | 需要重新编译 |
| Valgrind | 内存泄漏检测 | 无需重新编译 | 运行速度慢 |
| IDE 调试器 | 日常开发 | 可视化断点 | 依赖于 IDE |
5.3 调试实践
// 使用断言
#include <cassert>
void process(int* p) {
assert(p != nullptr);
}
// 调试日志
#define DEBUG 1
#ifdef DEBUG
#define LOG(x) cout << "[DEBUG] " << x << endl
#else
#define LOG(x)
#endif
// 异常处理
try {
// 可能出错的代码
} catch (const std::exception& e) {
cerr << "Error: " << e.what() << endl;
}