3、注释规范:文件头注释、函数注释、代码行注释的写法与审查要点

注释这件事,说大不大,说小不小。我见过太多团队,代码写得飞起,注释却一塌糊涂。说白了,注释就是代码的说明书。你想想看,一个产品没有说明书,谁敢用?

我个人习惯把注释分成三个层次:文件头、函数体、代码行。每个层次都有自己的规矩。咱们一个一个聊。

3.1 文件头注释:项目的身份证

每个源文件的开头,都应该有一块“身份证”。这块注释告诉别人:这个文件是干什么的,谁写的,什么时候改过。

我在项目中遇到过最头疼的事——一个驱动文件没有文件头注释,后来维护的人换了三拨,谁都不敢动那个文件。为什么?因为不知道它依赖什么模块,改了会不会炸。

标准文件头注释模板:

/*
 * 文件名称:uart_driver.c
 * 功能描述:STM32F4 UART驱动,支持中断收发
 * 作者:张工
 * 创建日期:2024-01-15
 * 修改记录:
 *   2024-03-20 李工 - 修复DMA模式下数据丢失问题
 *   2024-06-10 王工 - 增加波特率自适应功能
 *
 * 依赖关系:依赖hal_uart.h、ring_buffer.h
 * 编译条件:仅在STM32F4系列上编译
 */

审查要点有这几个:

  • 文件名称必须和实际文件名一致。我见过有人复制粘贴,文件名写错了,这种低级错误要扣分。
  • 修改记录要写清楚改了啥。别只写“修复bug”,要写“修复什么bug”。
  • 依赖关系必须标注。尤其是跨模块的依赖,你不写清楚,别人不敢动你的代码。

注意:文件头注释不是摆设。每次修改代码,记得同步更新修改记录。我曾经因为忘了更新,导致两个版本的文件头注释完全对不上,排查问题浪费了半天。

3.2 函数注释:API的使用说明书

函数注释比文件头注释更重要。因为函数是代码的基本单元,别人调用你的函数,第一眼看的不是代码,而是注释。

我建议每个函数都要有注释,哪怕是一个简单的getter函数。为什么?因为注释里可以写清楚边界条件、返回值含义、可能抛出的异常。

标准函数注释模板:

/**
 * @brief  通过UART发送数据
 * @param  data    待发送的数据缓冲区指针
 * @param  len     数据长度(字节),最大不超过256
 * @param  timeout 超时时间(毫秒),0表示不等待
 * @return int     成功返回发送的字节数,失败返回-1
 * @note   该函数会阻塞当前任务,直到发送完成或超时
 * @warning data指针不能为NULL,len不能为0
 */
int uart_send_data(uint8_t *data, uint16_t len, uint32_t timeout);

审查要点:

  • 参数说明要写清楚取值范围。比如len最大256,你不写,别人传个1024进来,缓冲区溢出就麻烦了。
  • 返回值要明确。成功返回什么,失败返回什么,别模棱两可。
  • 特殊行为要标注。比如这个函数会阻塞,或者会修改全局变量,一定要写清楚。

小技巧:函数注释最好用Doxygen格式。这样可以直接生成API文档,省时省力。我团队里强制要求所有公开函数必须用Doxygen注释。

3.3 代码行注释:解释“为什么”而不是“是什么”

代码行注释是最容易被滥用的。很多人喜欢写这样的注释:

// 将变量a加1
a = a + 1;

这种注释就是废话。代码本身已经说明了“是什么”,注释应该解释“为什么”。

我曾经审查过一个代码,里面全是这种废话注释。我直接打回去重写。你想想看,如果代码需要这种注释才能看懂,那说明代码本身写得不够清晰。

好的代码行注释示例:

// 这里加1是因为硬件寄存器从0开始计数,但我们的逻辑从1开始
a = a + 1;

// 等待硬件就绪,最多等100ms,否则认为硬件故障
while (!(REG_STATUS & 0x01)) {
    if (++timeout > 100) {
        error_handler(HW_TIMEOUT);
        break;
    }
    delay_ms(1);
}

审查要点:

  • 注释要解释“为什么”。比如为什么加1,为什么等100ms,这些是代码本身看不出来的。
  • 不要注释显而易见的代码。比如i++这种,谁都知道是自增。
  • 复杂逻辑一定要注释。尤其是位操作、状态机跳转、算法实现,这些地方不写注释,三个月后你自己都看不懂。

避坑指南:我曾经在一个项目中看到这样的注释——// 这里不要动,动了会出问题。结果后来排查问题,谁都不敢动那几行代码。最后发现,那几行代码根本就是多余的。所以,注释要写清楚“为什么不能动”,而不是简单地说“不要动”。

3.4 注释的审查清单

最后,我整理了一个注释审查清单,你可以直接拿来用:

审查项 要求 扣分标准
文件头注释 包含文件名、功能、作者、日期、修改记录 缺少一项扣1分
函数注释 包含功能、参数、返回值、注意事项 缺少一项扣2分
代码行注释 解释“为什么”,不写废话 废话注释扣1分,缺少必要注释扣2分
注释格式 统一使用Doxygen或C风格,不混用 格式不统一扣1分
注释更新 代码修改后同步更新注释 注释与代码不一致扣3分

嗯,注释规范就聊这么多。记住一句话:好的注释不是写给编译器看的,是写给下一个维护者看的。而下一个维护者,很可能就是三个月后的你自己。