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