崩溃之前,先认出这五个陷阱
这一课只练一件事:看到一段快出错的代码,先说出它属于哪一类语言层面的失分点,再选出正确修法。五类陷阱是、、、与混淆、以及没删干净的调试输出。必会:能识别这五类陷阱;能为大乘积选择;能检查数组访问是不是在 0 到 n-1 之内。最小示例:`int total = 50000 * 50000;` 编译能过,但 total 变成负数——这是溢出,不是越界。边界是范围表和精确位数是拓展,现在只要求先看规模。建议掌握:提交前删掉之外的调试文字并再测一遍。拓展:把五类陷阱整理成赛前检查清单,只是习惯,不是新语法。这里不会在浏览器里运行 C++。
任务 1:诊断两张故障卡
先判断第一张故障卡是不是,再判断第二张是不是。点不认识的词可看基础解释。
必会:能说出为什么 `int` 乘积会变负数;能说出合法下标为什么是 0 到 n-1。这里不会在浏览器里运行 C++,只练习判断和选择。
常见的竞赛环境里 int 是 32 位。`int total = 50000 * 50000;` 编译能过,但 total 变成了负数,这是为什么?溢出 · 必会
数组 a 长度是 n,写成 `for (int i = 0; i <= n; i++) a[i] = i;`,跑到最后一次就崩溃,这是为什么?数组越界 · 必会
任务 2:按定位、改型、回归三步走完改型卡
最小示例:一段程序用 `int sum` 累加 100000 项、每项 80000,手算总和 8000000000(正数,80 亿),程序却输出 -589934592。按顺序点三步:先,再改成,最后回归验证。这里不会在浏览器里运行 C++。
必会:能解释每一步为什么这样做。边界:不要只改一处就当作修完;不要改完不回归。
- // 现象:手算总和 8000000000(正数 80 亿),程序却输出 -589934592
- // 定位:sum 和累加中间量都声明成了 int,加起来超过 int 上限
- // 改型:把 sum 和中间量都换成 long long
- // 回归:用最大数据和最小数据各跑一遍,确认不再溢出
三步走完了,现在把“改型”落成一行代码:给累加变量选一个装得下 80 亿的类型,并让它从 0 开始。下面是只读脚手架,只留一空给你敲键盘;这里不会在浏览器里运行 C++。
- // 现象:n=100000,每项 80000,手算总和 8000000000(正数),程序却输出 -589934592
- int a[100005];
- int main() {
- int n;
- cin >> n;
- for (int i = 0; i < n; i += 1) cin >> a[i];
- // 改型:让 sum 装得下 80 亿
- for (int i = 0; i < n; i += 1) {
- sum += a[i];
- }
- cout << sum << "\n";
- return 0;
- }
long long sum = 0; // 类型、名字、初值、分号,四样都要对上
任务 3:优先级、比较符号与调试残留
必会:能分清与带来的错误。建议掌握:提交前删掉多余提示,只留下。
必会 / 建议掌握:必会是识别 `=` 和 `==` 混淆、判断优先级是否需要括号。建议掌握是删干净调试输出并回归测试。
- // 必会:if (a = 5) 永远为真,因为 = 是赋值,不是比较
- // 必会:a + b / 2 会先算 b / 2,想先加就要写 (a + b) / 2
- // 建议掌握:删掉 cout << "请输入 n:"; 这类调试残留
- // 边界:删完输出后要用原样例再跑一遍,不能只看编译通过
先选出这段代码为什么总是执行 if 分支:
再选出提交前发现残留调试输出该怎么办:
任务 4:随机小测,看看你是否真的懂了
每次从本课题库抽出三道不同的选择题。题目与解析已通过校验,答完后看解析,不要只看分数;拿不准就回到对应任务再看一遍。