import{_ as e,c as l,o,a4 as i}from"./chunks/framework.DtvhUNIn.js";const f=JSON.parse('{"title":"调试理论","description":"","frontmatter":{},"headers":[],"relativePath":"技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1.1调试理论.md","filePath":"技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1.1调试理论.md"}'),a={name:"技术资源汇总(杭电支持版)/3.编程思维体系构建/3.4.7.1.1调试理论.md"},r=i('
🌲 调试公理
The machine is always right. (机器永远是对的)
Every line of untested code is always wrong. (未测试代码永远是错的)
这两条公理的意思是:抱怨是没有用的,接受代码有 bug 的现实,耐心调试.
😋 如何调试
不要使用"目光调试法", 要思考如何用正确的工具和方法帮助调试
使用 assert() 设置检查点,拦截非预期情况
assert(p != NULL) 就可以拦截由空指针解引用引起的段错误结合对程序执行行为的理解,使用 printf() 查看程序执行的情况 (注意字符串要换行)
printf() 输出任意信息可以检查代码可达性:输出了相应信息,当且仅当相应的代码块被执行printf() 输出变量的值,可以检查其变化过程与原因使用 GDB 观察程序的任意状态和行为

如果我们能判定任意程序状态的正确性,那么给定一个 failure,我们可以通过二分查找定位到第一个 error 的状态,此时的代码就是 fault (bug)。
ssh:使用 -v 选项检查日志
gcc:使用 -v 选项打印各种过程
make:使用 -n 选项查看完整命令
make -nB | grep -ve '^\\(echo\\|mkdir\\)' 可以查看完整编译 nemu 的编译过程各个工具普遍提供调试功能,帮助用户/开发者了解程序的行为
我们来简单梳理一下段错误发生的原因。首先,机器永远是对的。如果程序出了错,先怀疑自己的代码有 bug . 比如由于你的疏忽,你编写了 if (p = NULL) 这样的代码。但执行到这行代码的时候,也只是 p 被赋值成 NULL, 程序还会往下执行。然而等到将来对 p 进行了解引用的时候,才会触发段错误,程序彻底崩溃。
我们可以从上面的这个例子中抽象出一些软件工程相关的概念:
if (p = NULL)p 被错误地赋值成 NULL调试其实就是从观测到的 failure 一步一步回溯寻找 fault 的过程,找到了 fault 之后,我们就很快知道应该如何修改错误的代码了。但从上面的例子也可以看出,调试之所以不容易,恰恰是因为:
理解了这些原因之后,我们就可以制定相应的策略了: