25、代码审查:审查清单设计、常见缺陷模式、审查流程、自动化审查工具

代码审查这事儿,我做了快二十年了。说实话,刚入行那会儿我觉得审查就是走个过场——代码能跑就行,看那么仔细干嘛?直到有一次,我漏掉了一个空指针检查,线上直接挂了。嗯,从那以后,我再也不敢小看代码审查了。

今天咱们聊聊代码审查的四个核心话题:审查清单怎么设计、常见缺陷有哪些、审查流程怎么走、以及自动化工具怎么用。说白了,就是帮你建立一套靠谱的审查体系。

一、审查清单设计:别靠脑子记,靠清单查

我个人习惯,审查前先列个清单。为什么?因为人脑会漏。你想想看,一个函数几百行,你盯着屏幕看十分钟,眼睛就花了。这时候有个清单,逐项检查,效率高得多。

我设计的审查清单,一般分这么几类:

类别 检查项 说明
内存安全 指针是否判空?malloc后是否检查返回值?free后是否置NULL? 这是C语言的头号杀手
边界检查 数组下标是否越界?循环终止条件是否正确? 缓冲区溢出大多源于此
资源管理 文件句柄是否关闭?锁是否释放?中断是否恢复? 资源泄漏是慢性病
错误处理 返回值是否检查?错误码是否覆盖所有分支? 别让错误静默传播
并发安全 共享变量是否加锁?临界区是否最小化? 多线程的坑,踩过才知道
我的小技巧: 清单不是死的。每次审查完,如果发现清单里没覆盖到的问题,就加进去。迭代个三五次,你的清单就越来越全了。

二、常见缺陷模式:这些坑,我几乎都踩过

代码审查多了,你会发现缺陷是有规律的。我总结了几种最常见的模式,你审查时重点盯着看:

1. 空指针解引用

这个太常见了。我曾经在一个项目中,有个函数返回了NULL,调用方没检查,直接用了。结果呢?段错误。修复就加了一行 if (ptr == NULL) return -1;,但排查花了半天。

// 错误写法
void process_data(Data *d) {
    d->value = 10;  // 如果d是NULL,直接崩
}

// 正确写法
int process_data(Data *d) {
    if (d == NULL) {
        return -1;  // 防御式检查
    }
    d->value = 10;
    return 0;
}

2. 缓冲区溢出

C语言没有自动边界检查,所以全靠程序员自觉。我见过最离谱的,是一个 char buf[10],然后 sprintf(buf, "%s", user_input)。用户输入稍微长一点,栈就被破坏了。

警告: 永远不要用 sprintfstrcpygets 这类不检查边界的函数。用 snprintfstrncpy 替代。

3. 资源泄漏

打开文件忘了关,申请内存忘了释放。这种问题在嵌入式系统里尤其致命——内存就那么点,泄漏几次就耗光了。我习惯在审查时,每个 malloc 都要找到对应的 free,每个 fopen 都要找到对应的 fclose

4. 错误码被忽略

很多函数返回错误码,但调用方直接无视了。比如:

// 错误写法
write(fd, buf, len);  // 返回值呢?写失败了你不知道

// 正确写法
ssize_t ret = write(fd, buf, len);
if (ret != len) {
    // 处理错误
}

三、审查流程:别走形式,走心

我参与过很多团队的审查流程。有的团队走形式,代码发出来没人看,直接点通过。有的团队又太死板,每行代码都要争论半天。我觉得好的流程应该是这样的:

  1. 提交前自审:开发者自己先过一遍,把明显的问题修掉。别把垃圾丢给别人看。
  2. 指定审查者:不要随机分配。找熟悉这块代码的人,或者找有经验的人。
  3. 逐行审查:审查者按清单逐项检查。发现问题就记录,不要当场改——改代码是开发者的事。
  4. 反馈与修改:审查者把问题列出来,开发者逐一修复。修复后最好再确认一遍。
  5. 合并:所有问题都解决了,才能合并到主分支。
关键点: 审查不是找茬,是帮团队提高代码质量。心态要正,语气要平和。我曾经见过因为审查吵起来的,没必要。

四、自动化审查工具:让机器干机器的活

人眼会累,机器不会。所以我把重复性的检查交给工具,人只做需要判断力的审查。

常用的自动化工具:

工具 用途 我的评价
cppcheck 静态分析,检查内存泄漏、空指针等 轻量级,适合嵌入式项目
Clang Static Analyzer 深度分析,能发现复杂路径上的问题 准确率高,但慢一点
Coverity 商业工具,功能全面 贵,但值得
SonarQube 代码质量平台,持续集成 适合团队使用

我建议的流程是:提交代码前,先跑一遍静态分析工具。工具报的警告,能修的修掉,不能修的加注释说明。这样审查者看到的代码,已经是经过一轮过滤的了。

注意: 工具不是万能的。它只能检查出模式匹配的问题,逻辑错误、设计缺陷这些,还得靠人。所以别完全依赖工具,人机结合才是最佳实践。

知识体系总览

下面这张图,我把代码审查的核心逻辑画出来了。你可以把它当作审查时的思维导图:

代码审查 审查清单设计 常见缺陷模式 审查流程 自动化审查工具 内存安全 边界检查 资源管理 错误处理 空指针 缓冲区溢出 资源泄漏 忽略错误码 自审 指定审查者 逐行审查 反馈修改 cppcheck Clang SA

这张图把审查的四个维度串起来了。你审查时,可以对照着看:清单有没有覆盖?缺陷模式有没有遗漏?流程走对了没?工具用上了没?


公众号:蓝海资料掘金营,微信deep3321