C++ 竞赛入门
测试与调试
用小数据和关键状态把错误变成可观察的问题。
测试与调试
本节学会什么
- 为程序准备样例、最小、边界、重复和零答案输入
- 用少量
cout观察循环变量和关键状态 - 用手算和反例定位常见的下标错误
为什么需要它
WA 很少会直接告诉你哪一行错了。可靠的做法不是反复猜,而是准备一个足够小的输入,让程序的每一步都能被看见。小数据能让你同时手算和输出关键变量,程序与手算第一次不同的地方,就是最值得检查的地方。
最小可运行示例
下面程序读入 n 个整数并求和。为了检查循环是否走对,在本地暂时输出下标和当前和:
#include <iostream>
int main() {
int n;
int a[100];
int sum = 0;
std::cin >> n;
for (int i = 0; i < n; i += 1) {
std::cin >> a[i];
sum += a[i];
std::cout << "i=" << i << ", sum=" << sum << '\n';
}
std::cout << "answer=" << sum << '\n';
return 0;
}
输入 3 和 2 5 1 时,手算的累计和应为 2、7、8。确认一致后,删除循环中的调试 cout,只保留题目需要的答案输出。
关键过程与易错点
- 固定检查五类数据:题目样例、最小输入、边界输入、包含重复值的输入、答案为
0的输入。不是每题都有所有类型,但应主动寻找。 n个元素的下标是0到n - 1。循环条件写成i <= n会多执行一次;当它访问a[i]时,最后一次已经访问了错误的a[n]。- 先只输出一个或两个关键变量,例如
i、累计和、当前最优值。输出所有变量通常只会制造更多噪声。 - 调试输出会改变正式输出格式。提交前应删掉或注释掉所有临时
cout,只留下题目要求的内容。 - 编译时保留
-Wall -Wextra。它能发现一部分未使用变量、可疑比较等问题,但不能替你证明算法正确。
动手检查
对于“读入 n 个数”的循环,先不访问数组,只分别写出 i < n 和 i <= n 时 i 的取值。取 n = 3,你应得到前者为 0、1、2,后者为 0、1、2、3。
再给下面的错误条件构造反例:把处理 n 个元素的循环从 i < n 写成 i <= n。选择最小的 n,手算哪一次循环的下标不属于这 n 个元素;不要依赖越界访问“刚好没有崩溃”的偶然结果。
立即练习
| 难度 | 题目 | 题解 | 训练目标 |
|---|---|---|---|
| 基础 | 洛谷 P1428:小鱼比可爱 | 题解 | 先用全相等数据和递减数据检查两层循环的边界。 |
| 巩固 | 洛谷 P1046:陶陶摘苹果 | 题解 | 为计数程序构造“全部能摘、全部不能摘、恰好相等”三类输入。 |