C++: 调试基础与常见错误
最后更新:2026-08-26
写代码不可能一次就写对,出错是常态。
真正的高手不是"不犯错",而是"能快速找到并修复错误"。
这一课,我们来学习如何调试 C++ 程序。
1. 三种错误类型
C++ 程序有三种错误,它们的发现时机和修复难度都不同。
| 错误类型 | 发现时机 | 修复难度 | 示例 |
|---|---|---|---|
| 编译错误 | 编译时 | ⭐(最容易) | 少了分号、括号不匹配 |
| 运行时错误 | 运行时 | ⭐⭐(中等) | 除以0、数组越界 |
| 逻辑错误 | 运行后(结果不对) | ⭐⭐⭐(最难) | 算法写错、条件写反 |
2. 编译错误(Compiler Error)
(1) 2.1 特点
- 发现时机: 编译时(输入
g++ xxx.cpp时) - 表现: 编译器直接报错,不生成可执行文件
- 修复: 根据错误信息,找到对应行,修复语法问题
(2) 2.2 常见编译错误
错误一:少了分号
▶ 示例 2:基础编程实践(难度⭐)
#include <iostream>
int main() {
int x = 5 // ❌ 少了分号
std::cout << x << std::endl;
return 0;
}
输出:
(程序输出)
编译错误信息:
error: expected ';' before 'std'
💡 技巧: 编译器说"在 std 前少了分号",但真正少分号的是上一行(第4行)。
错误二:括号不匹配
#include <iostream>
int main() {
if (5 > 3 { // ❌ 少了右括号
std::cout << "5 > 3" << std::endl;
}
return 0;
}
编译错误信息:
error: expected ')' before '{' token
💡 技巧: 用 VS Code 的括号匹配功能(光标放在括号上,对应的括号会高亮)。
错误三:变量未声明
#include <iostream>
int main() {
x = 5; // ❌ x 没有声明
std::cout << x << std::endl;
return 0;
}
编译错误信息:
error: 'x' was not declared in this scope
错误四:类型不匹配
#include <iostream>
int main() {
int x = 3.14; // ⚠️ 警告:把 double 赋给 int,会丢失小数部分
std::cout << x << std::endl;
return 0;
}
编译警告信息:
warning: narrowing conversion of '3.1400000000000001e+0' from 'double' to 'int'
💡 建议: 把警告当成错误处理!用 g++ -Wall -Wextra -Werror 编译,让警告变成错误。
3. 运行时错误(Runtime Error)
(1) 3.1 特点
- 发现时机: 编译通过,但运行时崩溃
- 表现: 程序突然退出,或者输出乱码
- 修复: 需要调试,找到导致崩溃的那行代码
(2) 3.2 常见运行时错误
错误一:除以 0
#include <iostream>
int main() {
int x = 5;
int y = 0;
std::cout << x / y << std::endl; // ❌ 除以 0,运行时崩溃
return 0;
}
运行结果:
Floating point exception (core dumped) // Linux
或
进程已结束,退出代码 -1073741676 // Windows
💡 修复: 在除法前检查除数是否为 0。
错误二:数组越界
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
std::cout << arr[10] << std::endl; // ❌ 访问了不存在的元素
return 0;
}
运行结果:
0 // 输出垃圾值,或者程序崩溃
💡 提示: C++ 不会自动检查数组越界!这是 C++ 速度快的原因之一,但也是常见的 bug 来源。
错误三:未初始化变量
#include <iostream>
int main() {
int x; // ❌ 没有初始化
std::cout << x << std::endl; // 输出垃圾值
return 0;
}
运行结果:
32767 // 输出垃圾值(每次运行可能不同)
💡 修复: 声明变量时就初始化。
4. 逻辑错误(Logic Error)
(1) 4.1 特点
- 发现时机: 程序能运行,但结果不对
- 表现: 没有明显的错误提示,需要自己对比预期结果和实际结果
- 修复: 最难!需要仔细检查代码逻辑
(2) 4.2 常见逻辑错误
错误一:条件写反
#include <iostream>
int main() {
int age;
std::cout << "请输入年龄:";
std::cin >> age;
if (age < 18) { // ❌ 写反了!应该是 age >= 18
std::cout << "你已经成年了" << std::endl;
} else {
std::cout << "你还未成年" << std::endl;
}
return 0;
}
💡 调试技巧: 用 cout 输出关键变量的值,看看程序走到了哪里。
错误二:循环条件写错
#include <iostream>
int main() {
// ❌ 想输出 1-10,但条件写成了 i < 10,只会输出 1-9
for (int i = 1; i < 10; i++) {
std::cout << i << " ";
}
std::cout << std::endl;
return 0;
}
错误三:= 和 == 写反
#include <iostream>
int main() {
int x = 5;
if (x = 10) { // ❌ 这是赋值,不是比较!x 变成 10,条件永远为真
std::cout << "x 是 10" << std::endl;
}
return 0;
}
💡 防坑技巧: 把常量写在左边:if (10 = x) 会编译报错。
5. 调试方法
(1) 5.1 用 cout 调试(最简单)
在关键位置加 cout,输出变量的值,看看程序是否按预期执行。
#include <iostream>
int main() {
int x = 5;
int y = 10;
std::cout << "[DEBUG] x = " << x << std::endl; // 调试输出
std::cout << "[DEBUG] y = " << y << std::endl;
int sum = x + y;
std::cout << "[DEBUG] sum = " << sum << std::endl;
std::cout << "结果:" << sum << std::endl;
return 0;
}
💡 技巧: 调试完成后,记得删掉(或注释掉)调试输出。
(2) 5.2 用 IDE 调试器(最强大)
VS Code + C/C++ 扩展自带调试器,可以:
- 设置断点(让程序暂停在某一行)
- 单步执行(一行一行地运行)
- 查看变量(实时看变量的值)
使用步骤:
- 在 VS Code 里,点击行号左边的空白处,设置断点(会出现一个红点)
- 按
F5(或点击"运行和调试"),选择"C++ (GDB/LLDB)" - 程序会运行到断点处暂停
- 按
F10单步跳过,或F11单步进入 - 在左侧"变量"窗口,可以看到所有变量的值
💡 提示: 调试器是终极武器,但初学者可能觉得复杂。建议先学会用 cout 调试,再学调试器。
6. 常见错误汇总
(1) 6.1 编译错误速查表
| 错误信息 | 原因 | 修复 |
|---|---|---|
expected ';' before ... |
少了分号 | 在上一行末尾加分号 |
expected ')' before ... |
少了右括号 | 补全括号 |
was not declared in this scope |
变量未声明 | 先声明变量 |
undefined reference to ... |
函数未定义 | 检查函数名是否拼写错误 |
(2) 6.2 运行时错误速查表
| 错误表现 | 原因 | 修复 |
|---|---|---|
| 程序突然退出 | 除以 0 | 在除法前检查除数 |
| 输出乱码/垃圾值 | 数组越界 | 检查数组下标范围 |
| 输出奇怪的大数 | 变量未初始化 | 声明时就初始化 |
(3) 6.3 逻辑错误速查表
| 错误表现 | 原因 | 修复 |
|---|---|---|
| 结果不对 | 条件写反 | 仔细检查 if 条件 |
| 循环次数不对 | 循环条件写错 | 用笔在纸上模拟循环执行过程 |
| 永远执行不了 | 把 == 写成了 = |
把常量写在左边 |
7. 实战:调试一个有 bug 的程序
▶ 示例 1:找出程序中的错误(难度⭐⭐)
有 bug 的程序:
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
// 想计算数组所有元素的和
int sum = 0;
for (int i = 0; i <= 5; i++) { // ❌ Bug 1:i <= 5 会导致越界
sum += arr[i];
}
std::cout << "数组元素的和:" << sum << std::endl;
int x = 5;
if (x = 10) { // ❌ Bug 2:= 应该是 ==
std::cout << "x 是 10" << std::endl;
}
return 0;
}
输出:
数组元素的和:15
x 不是 10
调试过程:
- 第一次运行: 程序崩溃(数组越界)
- 仔细检查
for循环,发现i <= 5应该是i < 5
- 第二次运行: 程序输出"x 是 10"(即使 x 是 5)
- 发现
if (x = 10)是赋值,改成if (x == 10)
修复后的程序:
#include <iostream>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int sum = 0;
for (int i = 0; i < 5; i++) { // ✅ 修复:i < 5
sum += arr[i];
}
std::cout << "数组元素的和:" << sum << std::endl;
int x = 5;
if (x == 10) { // ✅ 修复:==
std::cout << "x 是 10" << std::endl;
} else {
std::cout << "x 不是 10" << std::endl;
}
return 0;
}
▶ 示例 3:调试打印技巧(难度⭐)
#include <iostream>
int main() {
int sum = 0;
for (int i = 1; i <= 5; i++) {
sum += i;
// 调试打印:显示每次循环的值
std::cout << "[DEBUG] i=" << i << ", sum=" << sum << std::endl;
}
std::cout << "最终结果:" << sum << std::endl;
return 0;
}
输出:
[DEBUG] i=1, sum=1
[DEBUG] i=2, sum=3
[DEBUG] i=3, sum=6
[DEBUG] i=4, sum=10
[DEBUG] i=5, sum=15
最终结果:15
预期输出:
[DEBUG] i=1, sum=1
[DEBUG] i=2, sum=3
[DEBUG] i=3, sum=6
[DEBUG] i=4, sum=10
[DEBUG] i=5, sum=15
最终结果:15
❓ 常见问题
.exe 一闪而过是正常的,应在终端运行;②程序进入了死循环,按 Ctrl+C 强制终止后检查循环条件。launch.json。重点检查:是否安装了 gdb、launch.json 的 program 路径是否正确。std::endl 或 std::flush 强制刷新缓冲区即可。📖 小节
- C++ 程序有三种错误:编译错误、运行时错误、逻辑错误
- 编译错误最容易修(根据错误信息改)
- 运行时错误需要仔细检查(除以 0、数组越界)
- 逻辑错误最难找(用
cout或调试器) - 调试技巧:
- 用
cout输出关键变量的值 - 用 IDE 调试器设置断点、单步执行
- 把警告当成错误(
-Wall -Wextra -Werror)
📝 作业
- 基础题 (Difficulty ⭐): 把下面的程序改对(有 5 个 bug):
#include <iostream>
int main() {
int x = 5
int y = 10;
if (x = y) {
std::cout << "x 和 y 相等" << std::endl;
}
for (int i = 0; i <= 10; i++) {
std::cout << i << " ";
}
return 0;
}
-
进阶题 (Difficulty ⭐⭐): 编写一个程序,让用户输入 10 个整数,找出最大值和最小值。
-
用
cout调试,输出每次循环时变量的值 -
故意写一个 bug(比如初始化最大值时用了错误的值),然后用调试找出问题
-
挑战题 (Difficulty ⭐⭐⭐): 学习使用 VS Code 的调试器:
-
在
practice-branch-loop.md的猜数字游戏代码里设置断点 -
按
F5启动调试 -
用"单步执行"一步步运行,观察变量
secretNumber、guess、attempts的值 -
截图发给我看!
- 三种错误:编译错误、运行时错误、逻辑错误
- 编译错误最容易修,根据错误信息改
- 运行时错误:除以 0、数组越界
- 逻辑错误最难找:用 cout 或调试器逐步排查
- 编译时加 -Wall -Wextra 开启所有警告
8. 🚀 下一步
恭喜你!Phase 1(入门)的所有课程都学完了。 你现在已经掌握了 C++ 的基础知识——变量、数据类型、运算符、条件判断、循环、调试。
接下来我们进入 Phase 2(函数),学习如何把代码封装成可复用的模块,让程序更简洁、更易维护!