预防、检测及消除 bug | 读书笔记 | 《Intermediate C Programming》
开发软件 ≠ 编码
编码:在文本编辑器中输入语句。
编码只是软件开发中的一个小部分。在输入代码前,首先要知道为什么开发这个软件,弄清开发任务的目的。开发软件需要编程前、编程中、编程后的许多步骤。
编程前
- 阅读说明并了解要求
- 考虑可能的输入和期望的输出
- 识别出有效但非期望的输入和正确的输出
- 识别出无效的输入并找到检测它们的方法,如在整数序列中出现浮点数
- 想出解决措施并写下方法
- 画出框图来显示信息在程序的不同部分之间进行交换
- 计划好程序的实现层面:创建多少个函数?每个函数做什么?需要多少个文件?
- 每个函数只做一件事,且不能太长
- 详细的设计,将会节省在编码和调试中的时间
编程中
- 单元测试可以提高开发效率——写小程序来测试解决方案。保证在继续写下去之前,已写的代码能正常工作
- 总是使用能自动缩进的文本编辑器
- 运行用例之前一行一行地读代码
- 在大脑中运行简单的测试用例
- 在进行之前,测试某些条件是否已满足,如要排序,得先测试输入是否正确
- 避免复制粘贴代码,通过创建函数来
重构
代码,降低修改成本 - 使用版本控制
- 解决所有编译警告
编程后
阅读程序,不依赖测试,检查有没有常见错误。
常见错误
- 未初始化变量
- 数组下标越界
- 错误数据类型
后执行式和交互式调试
调试程序的一个策略,是把程序分为几个阶段,在每一阶段的基础上隔离问题。在把不同部分整合起来之前确保程序在每一阶段都是正确的。例:①从一个文件中读取一些整数;②把整数排序;③把排好序的整数存到另一个文件中。
对于单元测试,经常需要一些额外的代码作为单独部分的“驱动”。例如,要在还未从文件中获得数据的情况下测试排序是否能正确运行,就需要编写能够产生数据的代码(比如随机数生成器)。
后执行式调试,通过日志获取调试信息;交互式调试,即单步调试。
以下情况推荐使用后执行式调试:
- 程序运行需要大量时间
- 程序运行时需要与另一个程序用过网络进行通信
- 与实体世界关联(例如控制机器人)
在许多其他情况下,可以让程序慢下来,对程序进行交互式调试。打印调试信息存在很多问题:
- 需要插入大量代码来打印调试信息
- 缺少信息则难以判断错误的地方
- 信息太多,则有可能存在垃圾信息混淆视听
- 问题可能出在意想不到的地方,而那里没有插入调试信息
生产代码与测试代码分离
建议编写可以检测出自身Bug的程序。想检查一个数组是否被排好序,不必打印出所有元素,而可以写一个只输出true和false的程序。
应该在编写程序之前就考虑编写测试代码,这叫测试驱动开发
。
生产代码之外编写测试代码,能有效降低移除测试代码的成本。