经验之谈

编程前


  • 阅读说明, 了解要求
  • 考虑 可能输入 和 期望输出
  • 识别 有效但非期望的输入 以及 正确的输出
  • 比如排序程序, 如果输入是已排序的, 该如何处理? 相同的数字多次出现, 如何处理? ...

  • 识别 无效输入, 并找到检测办法, 即使输入无效, 程序也不能崩溃
  • 想好解决措施, 在纸上写出方法
  • 画出框图, 显示信息如何在程序不同部分之间进行交换
  • 有了设计后, 计划好实现层面
  • 创建多少个函数? 每个函数做什么? 需要多少个文件? ...

  • 建议: 每个函数只做一件事, 且不应太长, 最好控制在一页电脑屏幕就写完

编程中


  • 把代码提交到一个更大的程序之前, 写一个小程序, 测试你的代码, 这叫 单元测试
  • 如果不能保证一个函数正常工作, 那么, 把多个函数组合在一起还能正常工作就无从谈起.
    完成了程序的一部分, 在继续下去之前, 要确保写好的部分是可以正常工作的.

  • 运行测试前, 一行一行读自己写的代码, 在脑中运行一些简单的测试例子
  • 在继续之前, 写一些代码, 测试某些前置条件是否已满足
  • 比如, 假设排序是程序的一部分, 在程序做其他事之前, 先检查一下数据是否已保存好.

  • 避免复制粘贴代码, 创建函数来重构代码
  • 使用版本控制
  • 会有这样的情况, 昨天还正常, 做了些改动就无法工作了, 而改动太多, 改了哪里完全记不清了.
    版本控制可以让你知道与之前版本相比的改动.

  • 解决所有的编译警告
  • 一些人会忽略警告, 认为可以在程序运行之后再解决, 然而警告常常预示一些导致程序难以运行的问题.

编程后


  • 读一读程序, 检查有无常见错误, 不要依赖测试
  • 测试只会告诉你程序没有正常工作, 无法告诉你程序真的在正常工作, 测试用例无法覆盖所有可能.

    几个常见错误:

    • 未初始化变量
    • 错误的数组下标
    • 错误的数据类型

  • 生产代码 与 测试代码 分离
  • 应该编写检测 bug 的程序.
    比如, 要检查一个数组是否排序好, 不要打印然后靠眼睛观察, 应该写个函数.
    应该在编程前就考虑编写测试代码, 这叫 测试驱动开发.

    func 是被测函数, test_func 是测试 func 的代码.

    1. /*
    2. 函数内部调用测试代码
    3. 生产代码与测试代码混在一起, 想要移除很困难
    4. */
    5. func(args)
    6. {
    7. /* 运行, 得到结果 */
    8. /* 测试, 检验结果 */
    9. }

    1. /*
    2. 函数外部调用测试代码
    3. 生产代码与测试代码分离, 想要移除很简单
    4. */
    5. func(args)
    6. {
    7. /* 程序代码 */
    8. }
    9.  
    10. test_func(args)
    11. {
    12. /* 创建 参数 */
    13. res = func(参数);
    14. /* 检验 res */
    15. }

《 C 语言程序设计进阶教程》