元数据卡
- 前置知识:调试器基础 + 进阶
- 预计时间:30 分钟
- 阅读模式:动手操作
- 完成标志:完成三个热身和一个递归挑战
你的进度
你学会了断点、条件断点、调用栈、WATCH 面板。现在是时候动手了——把知识变成肌肉记忆。
常见陷阱(先看,避免掉坑)
故事一:断点打在了注释行
有一次我花了两小时,加断点、单步、看变量——啥都没错,但程序就是不停。最后发现,断点打在了注释行上。
IDE 允许你在注释上设断点——但它永远不可能被执行到。调试器不会提醒你"嘿,这行是注释",它只会尊重你的决定。
教训: 实心红点 = 有效断点。空心或带问号 → 打在无效位置了。
故事二:优化后的代码找不到行号
C/C++ 编译器开 -O2 优化时,可能重排指令、内联函数、删未用变量。你设的断点被编译器"优化没了"——跑过去不会停,因为那行代码在二进制里不存在了。
解决: 开发环境用 -O0 -g,发布环境才用 -O2。
热身(5 分钟,必做)
- 在 VS Code 中打开任意 Python 文件(新建一个也行)
- 在你熟悉的函数内第一行设置断点(点行号左侧)
- 按
F5以调试模式运行 - 按 F10(步过) 至少 5 行,观察 VARIABLES 面板的变化
- 在 WATCH 面板加一个表达式,比如
len(items)或你代码里有的变量运算
做完这 5 步,你就能在真实场景里直接上手调试了。
挑战:递归函数调试(30 分钟,选做)
这是带 bug 的阶乘函数。它永远不会返回正确结果:
# factorial_debug.py
def factorial(n):
# 这是有 bug 的版本
if n == 0: # 应该是 n == 1 或 n <= 1
return 1
return n * factorial(n - 1)
print(factorial(5))语言: Python 3 如何运行: python factorial_debug.py预期正确输出: 120(5×4×3×2×1) 实际输出(有 bug): 0(因为递归到底时 factorial(0) 触发 n == 0,但循环不会在 n=1 停住)
调试步骤:
- 在
return n * factorial(n - 1)行设断点 - 按 F5 运行,观察调用栈——每一次递归调用都往栈上压一层
- 给
n加条件断点n == 2——只在递归深度到 2 时停住 - 在 WATCH 面板输入
n * factorial(n - 1)——你会发现它显示<error>,因为递归函数还没返回时,表达式无法求值 - 通过观察
n的变化轨迹找出 bug:当n == 0时,函数返回 1,但n == 1时应该返回 1 而不是继续递归调用
排障场景
场景 1:断点打了,F5 按了,程序直接结束了,没停。
诊断: 可能的原因:
- (a) 断点打在了注释行、空行等无效位置
- (b) 运行的是错误的目标文件(不是当前编辑器里的文件)
- (c) C/C++ 编译优化把代码行抹掉了
解决: 确认红点实心,确认调试配置选对了入口,检查编译器参数。
场景 2:步进(F11)时跳进了标准库代码。
诊断: IDE 默认允许进入你安装的库代码。你一般不需要看 sorted() 的内部实现。
解决: VS Code 里搜 "Just My Code" 设置,开启后只调试你自己的代码。IntelliJ 的 Java 模式默认开启此功能。
验收标准
- [ ] 能理解断点的作用——在代码执行到指定行时暂停
- [ ] 会使用条件断点过滤不必要的暂停
- [ ] 能通过 VARIABLES / WATCH 面板观察变量值
- [ ] 能通过 CALL STACK 面板追溯调用来源
- [ ] 懂得 Step Over(步过)和 Step Into(步入)的区别
- [ ] 能在调试控制台求值和修改变量
- [ ] 知道日志断点(Logpoint)能替代临时 print() 调试
常见卡点
"步进(F11)和步过(F10)到底选哪个?"
- 步过:不进入函数内部。如果你不想看
sorted()的实现,用步过。 - 步进:跳进函数内部。如果你想看自己写的函数逻辑,用步进。
- 步过:不进入函数内部。如果你不想看
"调试控制台里改了变量,程序真的会变吗?"
- 会。你写
n = 100,程序里的n真的变 100 了。用来测边界可以,但记得恢复。
- 会。你写
"为什么看不到上层变量?"
- 调试器只显示当前栈帧。想看调用者的变量,在 CALL STACK 面板点击那层帧。
"断点太多,每次都要删吗?"
- 不需要。可以禁用断点(取消勾选),或一次清除所有断点。
旅人笔记
调试器不是报错的工具,是观察的工具。print() 像是推测,"这里应该有问题吧";断点像是目击,"我亲眼看到它干了什么"。调用栈让你看清"谁叫谁"的因果链——很多 bug 不是最后一行的错,而是谁把程序引到那条路上去的。
→ 下一站预告
调试器在手,bug 渐行渐远。但你的项目开始依赖越来越多的外部库——这些代码从哪里来?怎么安装?怎么保证你电脑上的版本和别人电脑上的一致?下一章,我们聊聊包管理器。