# 调试

> CIE A-Level 计算机科学 · 9618
> 来源: https://www.owlsprep.com/zh/study/cie-9618-u12-debugging/

本模块讲解 CIE A-Level 计算机科学的核心调试概念，包括程序错误类型、常用系统化方法，以及在软件代码中定位、诊断和解决漏洞的技术。

**先修:** [基础软件开发生命周期](https://www.owlsprep.com/zh/study/cie-9618-u12-software-development-lifecycle/); 基础编程概念

## 学习目标

- 对三种主要程序错误类型进行分类
- 描述常用调试方法和工具
- 应用系统化流程定位和修复漏洞
- 在考试中区分测试和调试

## 程序错误的类型

在开始调试之前，必须先对错误进行分类，这会指导你选择合适的调试方法。CIE 9618 经常考察考生区分三种核心程序错误类别的能力。

**核心程序错误类别** — 软件开发过程中遇到的三大错误类别，每种都有不同的症状，需要不同的调试方法。

*例:* 语法错误、运行时错误、逻辑错误

- **语法错误**：违反编程语言的语法规则，在执行开始前被编译器或解释器捕获，程序完全无法运行。
- **运行时错误**：在程序执行过程中发生的错误，会导致程序意外崩溃或中止，无法完成执行。
- **逻辑错误**：程序可以运行完成不崩溃，但会产生错误输出或出现意外行为。

**例题:** 对以下错误进行分类：一个 Python 程序将用户输入除以零，程序停止执行并输出`ZeroDivisionError`错误信息。

1. 步骤 1：确认程序已经成功编译/解释，开始执行后才失败。
2. 步骤 2：记录结果：程序意外停止执行，而非产生错误输出。
3. 步骤 3：将症状与错误类型定义进行匹配。
4. 结论：这是一个运行时错误。

> **考试提示:** 始终检查程序是否能运行：如果程序能运行但输出错误，那就是逻辑错误，不是运行时错误。

## 常用调试方法

调试经常被和测试混淆，但这两个过程的目标截然不同。测试确认漏洞存在；调试则定位并修复漏洞。有多种成熟方法可以高效定位漏洞。

**调试** — 定位、识别并解决计算机程序中漏洞（错误）的系统化流程。

*记法:* 测试发现漏洞；调试修复漏洞。

- **人工走查**：手动逐行跟踪代码，跟随执行流程和变量值。
- **打印语句调试**：添加临时打印语句，在代码关键点输出变量值。
- **二分查找调试**：反复将代码分成两半，高效缩小漏洞的位置范围。
- **IDE 工具调试**：使用 IDE 内置工具在运行时暂停执行，检查程序状态。

**例题:** 一个 100 行的程序在执行到一半时崩溃。你会如何使用二分查找来定位漏洞？

1. 步骤 1：在代码中点（第 50 行）添加一个打印语句或断点。
2. 步骤 2：运行程序。如果程序在到达第 50 行之前崩溃，说明漏洞在前 50 行；如果没有崩溃，说明漏洞在 51–100 行之间。
3. 步骤 3：对包含漏洞的那一半代码重复该过程，每次缩小范围。
4. 步骤 4：当你将漏洞范围缩小到单行或小块代码时停止。

## 调试工具

现代集成开发环境（IDE）包含强大的内置调试工具，比人工方法更快定位运行时错误和逻辑错误。CIE 要求你掌握最常用工具的功能。

**断点** — 设置在代码行上的标记，指示调试器在到达该行时暂停执行，允许你检查当前程序状态。

- **监视表达式**：添加到调试器监视列表的变量或表达式，每当执行暂停时都会显示其当前值。
- **步入（Step into）**：执行下一行代码，如果该行调用了函数，则在函数内部暂停执行。
- **步过（Step over）**：执行下一行代码，直接运行完所有被调用函数，不会在函数内部暂停。
- **步出（Step out）**：继续执行直到当前函数返回，然后再次暂停。

**例题:** 函数 `calculateTotal(price, tax)` 在输入 price=100，tax=0.2 时，返回了错误的总价 \$102，而预期值是 \$120。你会如何使用断点和监视功能找到错误？

1. 步骤 1：在 `calculateTotal` 函数内部的第一行设置一个断点。
2. 步骤 2：使用测试输入 price = 100，tax = 0.2 运行程序。
3. 步骤 3：执行暂停后，将 `price`、`tax` 和当前总价添加到调试器的监视列表。
4. 步骤 4：逐行单步执行函数的每一行，每次执行后检查总价，看它在哪一步偏离了预期值。
5. 步骤 5：找到错误行后，修复错误并重新测试函数。

## 系统化调试流程

有效的调试遵循可重复的系统化流程，而非猜测问题所在。这能减少耗时，确保漏洞被完全解决，不会引入新问题。

1. 使用最简测试用例稳定复现漏洞
2. 根据错误症状提出关于漏洞位置的假设
3. 通过检查代码和程序状态验证假设
4. 如果假设错误，提出新的假设并重复测试
5. 找到漏洞位置后，实施错误修复
6. 测试修复，确认漏洞已解决，且没有引入新漏洞

**概念自测**

检查你对核心概念的理解：

1. 系统化调试的第一步是什么？

   - 修复漏洞
   - 复现漏洞
   - 提出假设
   - 设置断点

   *解析:* 你必须首先能够稳定复现漏洞，才能正确对其进行调查。

2. 以下哪一项属于逻辑错误？

   - 缺少分号导致编译停止
   - 除零错误导致程序崩溃
   - 计算百分比时除以 10 而非 100，输出 1000% 而非 10%
   - Python 中忘记闭合括号

   *解析:* 逻辑错误可以运行不崩溃，但给出错误输出，符合该情况。

> **考试提示:** CIE 经常考察测试和调试的区别：记住测试发现漏洞，调试修复漏洞。

## 常见错误

- **错误做法:** 在考试答案中混淆测试和调试
  - 原因: 测试仅确认漏洞存在，不会定位或修复漏洞
  - 正确做法: 始终明确：测试识别漏洞是否存在，调试定位并解决漏洞
- **错误做法:** 因为输出错误就将逻辑错误归类为运行时错误
  - 原因: 运行时错误一定会导致程序崩溃或中止，逻辑错误不会
  - 正确做法: 如果程序可以运行完成但给出错误输出，那就是逻辑错误
- **错误做法:** 跳过复现漏洞，直接修改代码
  - 原因: 没有稳定复现，你无法确认你的修复是否真的生效
  - 正确做法: 开始调试前，始终先创建一个能复现漏洞的最简测试用例
- **错误做法:** 描述调试器时混淆步入和步过
  - 原因: 混淆这两个概念是常见的考试错误，会丢分
  - 正确做法: 步入会进入函数以便在内部调试；步过会直接运行完函数，不暂停
- **错误做法:** 修复漏洞后不重新测试
  - 原因: 修复一个漏洞可能会在程序其他部分引入新漏洞
  - 正确做法: 修复漏洞后始终运行所有相关测试，确认修复有效

## 速查表

| 类别 | 条目 | 考试核心要点 |
| --- | --- | --- |
| 错误类型 | 语法错误 | 执行前被捕获，程序无法运行 |
| 错误类型 | 运行时错误 | 执行过程中发生，导致程序崩溃 |
| 错误类型 | 逻辑错误 | 不崩溃，输出错误，最难调试 |
| 调试工具 | 断点 | 暂停执行以检查程序状态 |
| 调试工具 | 步入 | 在被调用函数内部暂停以便检查 |
| 调试工具 | 步过 | 运行完函数，不在内部暂停 |
| 调试方法 | 二分查找 | 在大型代码库中快速定位漏洞 |
| 流程步骤 | 第一步 | 稳定复现漏洞 |

## 下一步

调试是 CIE A-Level 计算机科学的核心实践和理论技能，建立在你对软件开发生命周期和程序设计的理解之上。你在这里学到的漏洞定位和解决的系统化方法适用于所有编程范式，对考试答题和你自己的实践编程作业都很有用。掌握调试后，你可以通过学习不同类型的软件测试、高级错误处理技术，以及支持协作开发环境中调试的版本控制工具，深化你对软件质量的知识。

- [软件维护](https://www.owlsprep.com/zh/study/cie-9618-u12-software-maintenance/)
- [软件项目管理](https://www.owlsprep.com/zh/study/cie-9618-u12-software-project-management/)
- [计算思维与问题解决（含声明式、面向对象和人工智能）](https://www.owlsprep.com/zh/study/cie-9618-u13-overview/)

---

来自 [OwlsPrep](https://www.owlsprep.com) —— A-Level / IB / AP / IGCSE 免费学习指南，依据官方考纲编写。原页面：https://www.owlsprep.com/zh/study/cie-9618-u12-debugging/
