4、注释规范(上):文件头注释、函数头注释、代码块注释

注释这东西,说大不大,说小不小。我见过不少团队,代码写得飞起,注释却一塌糊涂。结果呢?三个月后自己都看不懂自己写的啥。嗯,今天我们就来聊聊注释规范,重点讲文件头、函数头和代码块这三块。

核心观点:注释不是写给机器看的,是写给人看的。说白了,注释就是代码的「说明书」。

4.1 文件头注释:给每个文件贴个「身份证」

每个源文件,不管是 .c 还是 .h,都应该有个文件头注释。我个人习惯,文件头注释至少包含以下信息:

  • 文件名称:一目了然,别让人猜
  • 功能描述:这个文件是干嘛的,一句话说清楚
  • 作者:出了问题知道找谁(笑)
  • 创建日期:方便追溯历史
  • 版本号:配合 Git 标签使用更佳

我在项目中遇到过,有人把文件头注释写得跟小说似的,洋洋洒洒几百字。其实没必要,简洁明了就好。来看个例子:

/**
 * @file    uart_driver.c
 * @brief   STM32 UART 驱动层实现
 * @author  张三
 * @date    2024-01-15
 * @version v1.0.0
 *
 * @note    本文件实现 UART 初始化、发送、接收功能
 *          依赖 hal_uart.h 底层接口
 */

小技巧:文件头注释里的版本号,建议和 Git tag 保持一致。我习惯在发布版本时统一更新,避免出现「版本号对不上」的尴尬。

4.2 函数头注释:让调用者一眼看懂

函数头注释,说白了就是告诉别人:这个函数能干啥、需要啥、返回啥。我建议每个对外接口函数都要加,内部静态函数看情况。

为什么会这样?你想想看,一个项目几百个函数,如果没有注释,调用者得一个个翻实现代码,多痛苦。

函数头注释我一般包含这几项:

字段 说明 是否必须
@brief 函数功能简述 必须
@param 参数说明(含取值范围) 必须
@return 返回值含义 必须
@note 注意事项、调用约束 推荐

来看个实际例子:

/**
 * @brief   通过 UART 发送数据缓冲区
 * @param   huart   UART 句柄指针,不能为 NULL
 * @param   pData   待发送数据缓冲区指针
 * @param   size    发送字节数,范围 1~1024
 * @return  HAL_StatusTypeDef
 *          - HAL_OK:      发送成功
 *          - HAL_ERROR:   参数错误
 *          - HAL_BUSY:    上次发送未完成
 * @note    本函数为阻塞发送,超时时间由宏 UART_TX_TIMEOUT 控制
 *          调用前需确保 UART 已初始化
 */
HAL_StatusTypeDef UART_SendBuffer(UART_HandleTypeDef *huart,
                                  uint8_t *pData,
                                  uint16_t size);

注意:函数头注释不要写「实现细节」。比如「先关中断再发数据」这种话,应该写在函数内部注释里。头注释只告诉别人「怎么用」,不告诉别人「怎么实现」。

4.3 代码块注释:解释「为什么」而不是「是什么」

代码块注释,是我觉得最容易被忽视的地方。很多人喜欢写这样的注释:

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

嗯,这种注释,说实话,写了等于没写。代码本身已经说明「做了什么」,注释应该解释「为什么这么做」。

我曾经在一个老项目里看到这样的代码:

// 延迟 10ms,等待硬件稳定
HAL_Delay(10);

这个注释就很好。它解释了为什么需要延迟,而不是单纯描述「调用了 HAL_Delay 函数」。

代码块注释的使用场景,我总结了一下:

  • 复杂算法:解释算法思路,而不是逐行翻译
  • 特殊处理:为什么要做这个边界判断
  • 硬件相关:为什么这个寄存器要这么配
  • 临时方案:标记 TODO 或 HACK,说明原因

来看个实际例子:

/* 
 * 这里使用二分查找而非线性查找,原因:
 * 1. 数据表已按升序排列
 * 2. 表项数量超过 1000,线性查找太慢
 * 3. 二分查找最坏情况只需 10 次比较
 */
int idx = BinarySearch(key, table, tableSize);

我的习惯:代码块注释用 /* */ 风格,和代码区分更明显。单行注释用 //,多行注释用 /* */。这样扫一眼就知道注释的范围。

4.4 知识体系总览

下面这张图,是我整理的注释规范核心逻辑。你可以把它当作一个检查清单:

注释规范核心体系 文件头注释 函数头注释 代码块注释 文件名、功能描述、作者、日期、版本 @brief、@param、@return、@note 解释「为什么」而非「是什么」 核心原则 1. 注释是给人看的,不是给机器看的 2. 解释「为什么」,而不是「是什么」 3. 保持简洁,过犹不及

4.5 避坑指南

最后,分享几个我踩过的坑:

  • 注释和代码不一致:我曾经改完代码忘了更新注释,结果同事照着注释调了一天 bug。嗯,从那以后我养成了「改代码必改注释」的习惯。
  • 过度注释:每行代码都加注释,反而干扰阅读。注释应该「少而精」。
  • 注释里写小说:文件头注释写几百字,没人会看。控制在 5-8 行最合适。

曾经有个教训:我在一个项目里看到这样的注释——「这里不要动,动了会出问题」。结果三年后,没人知道为什么不能动,代码也不敢重构。所以,注释要写清楚「为什么」,而不是「别动」。

好了,注释规范的上半部分就聊到这里。记住,好的注释是代码的「说明书」,不是「装饰品」。写注释的时候,多想想「如果我是接手的人,我希望看到什么」。