很多人代码一些完,马上开始Make, 殊不知他错过了一次很好的机会。这个机会就是代码自查,它的目的不仅仅是为了发现程序的错误,更是为了认识到自己编程习惯的局限和盲点,从而在下次进行有效地改进。因为,人自己发现自己的错误并修改的过程,是一种自愈和自我免疫的过程。

1. 前提

满足了下面的前提,就可以开始代码自查:

    1. 项目需求已经确定,有可参考和检查的文档,
    1. 主要的功能,已经拆分为各个子模块;
    1. 和其他模块的接口,已经定义好;
    1. 每个模块的代码,已经用代码实现;

当然,如果你模块划分得越细,也可以逐模块开始代码自查。

2. 过程

可以做两轮自己的code review,每轮的侧重点不一样:

2.1 功能自查

第一轮检查,主要侧重于以下的方面的检查

2.1.1 文件级

重点检查:

  • 头文件是否能避免重复保护;
  • 如果需要支持C++,头文件是保护extern C;
  • 头文件中的extern 是否有定义;
  • 头文件中的函数是否有定义、定义是否一致;
  • 是否遵循最小暴露原则;
  • 在全局静态数组的定义中,使用了需要运行时才能确定的变量,导致了编译错误

2.1.2 子模块级

  • 子模块内部实现的逻辑是否符合预期;
  • 子模块对外的接口函数的参数和返回值是否恰当;
  • 是否有之和子模块相关的变量实现成了全局变量;

2.1.2 函数级

  • 输入输出参数是否冗余、缺失、类型不对;
  • 函数内部是否声明了不用的变量;
  • 函数返回和函数声明是否一致;
  • 参数检查是否遗漏或者过多;
  • 内部循环是否退出条件过若或者过强;
  • swtich/case 是否遗漏break、default,或者default不可能执行到
  • 申请的内存是否遗漏释放;
  • 对申请到的内存的使用是否越界;
  • 该函数是否在临界区,是否包含临界区,是否会死锁;
  • 功能逻辑是否符合预期

2.1.3 宏定义级

  • 是否有些近似的代码反复出现没有用宏替换
  • 是否把宏当做函数用了,忽略了宏定义只是简单的完全替换关系

2.1.4 拼写级

  • 有一个数据结构中的一个成员,只有类型,没有变量名称;
  • 遗漏分号、过多分号;
  • 拼写错误,特别是夹杂有大小写的情况:idLsu/idlsu lsfsret/LsfsRet

上面检查过程中,发现立即可以修改的马上修改,比较复杂的可以放到一个TODO list的FIFO里去;

2.2 性能检查

第二轮检查侧重于以下的方面:

2.2.0 算法

针对具体的功能和场景,区分是响应时间有限还是节省资源有限,列出所有可选的算法实现,确定是否当前是最合适的实现;

2.2.1 IO路径

  • IO路径上的所有操作是否都是必需在请求返回前执行的,如果不是,可以拆分到后台线程去执行;
  • IO路径上是否会全抢锁等待,如果有尽量去掉锁或者减小等待的时机;

2.2.2 并发的粒度

  • 是用多进程还是多线程去并行;
  • 是否需要绑定处理器核去执行;

2.2.3 锁的类型

  • 是否必需,能否用无锁队列替换;
  • 是否适用读写锁;

2.2.4 IO合并和放大

  • 每次是否有太多的没有改动的数据也落盘了;
  • 是否依赖了必需落盘的数据,导致它没及时落盘;

同样,如果上面有问题,也需要添加到一个TODO List里面去。

2.3 完成TODO List

  • 根据上面自查完成后生产的TODO list,把里面每一项完成;
  • 完成TODO list后,对照自己常犯错误的清单,再次自查,修改完成之后可以交由编译器去编译调试了。

如果此时,你发现编译器也没发现任何错误和警告,恭喜你,已经比较牛了