4、单行注释的艺术:什么时候该用//,什么时候不该用,避免过度注释
说实话,单行注释是C语言里最容易被滥用的东西。我见过不少项目,代码里密密麻麻全是//,读起来比看小说还累。你想想看,注释本来是为了让人看懂代码,结果注释太多反而成了噪音。
我个人习惯是:能用代码表达清楚的,绝不多写一行注释。这不是懒,这是对阅读者的尊重。
4.1 什么时候该用//
先说说哪些场景下,单行注释是真正有用的。
4.1.1 解释「为什么」而不是「是什么」
这是最核心的原则。代码本身已经说明了「它在做什么」,注释应该解释「它为什么这么做」。
// 正确示范:解释原因
uint32_t delay = 1000; // 等待1秒,确保传感器上电稳定
// 而不是:
uint32_t delay = 1000; // 定义延时变量为1000
我在项目中遇到过这样的坑:一个同事写了temp = temp + 273;,注释是「温度加273」。后来排查问题时,我花了半小时才反应过来这是摄氏转开尔文。如果注释写成「摄氏温度转开尔文温度」,一眼就明白了。
4.1.2 标记待办事项和已知问题
嵌入式开发中,有些代码是临时方案,或者存在已知缺陷。用// TODO、// FIXME、// HACK标记出来,方便自己和后人追踪。
// TODO: 这里需要处理DMA传输完成中断,当前用轮询方式替代
while (DMA_GetFlagStatus(DMA1_FLAG_TC1) == RESET);
// FIXME: 当系统时钟为72MHz时,该延时误差约3%,需要校准
delay_us(100);
// HACK: 硬件工程师说这个引脚电平不稳定,先读两次取平均值
uint8_t val = (GPIO_ReadPin(GPIOA, GPIO_PIN_0) + GPIO_ReadPin(GPIOA, GPIO_PIN_0)) >> 1;
我曾经接手过一个项目,里面有个// HACK: 别问我为什么,反正这样能跑。嗯,后来我花了三天才搞清楚这个「能跑」背后的真正原因。所以标记要具体,别偷懒。
4.1.3 解释复杂算法或非直观逻辑
有些代码逻辑确实绕,比如状态机跳转、位运算技巧、或者硬件相关的特殊处理。这时候注释就是救命稻草。
// 计算CRC校验时,多项式0x1021需要左移一位,因为硬件自动补0
uint16_t crc = crc_calc(data, len, 0x1021 << 1);
// 状态机:从IDLE跳转到SEND时,必须清空缓冲区,否则会发送旧数据
if (state == STATE_IDLE && event == EVENT_SEND) {
buffer_clear();
state = STATE_SEND;
}
4.1.4 分隔代码块,提升可读性
当一个函数比较长时,用单行注释做段落分隔,比空行更清晰。我习惯用// ===或者// ---来区分不同功能段。
// ========== 初始化部分 ==========
gpio_init();
uart_init();
timer_init();
// ========== 主循环 ==========
while (1) {
process_sensor_data();
update_display();
}
4.2 什么时候不该用//
这部分更重要。我见过太多「为了注释而注释」的代码,反而降低了可读性。
4.2.1 不要注释显而易见的代码
这是最常见的过度注释。代码本身已经足够清晰,注释就是废话。
// 错误示范:废话连篇
int a = 0; // 定义整型变量a,初始化为0
a = a + 1; // a自增1
if (a > 10) { // 如果a大于10
printf("a大于10"); // 打印提示信息
}
你想想看,任何一个学过C语言的人都能看懂这些代码。写这种注释,要么是不信任读者,要么是凑行数。我个人觉得,这是对同事智商的侮辱。
4.2.2 不要用注释代替好的命名
如果变量名、函数名本身就能说明问题,注释就是多余的。
// 错误示范:命名糟糕,靠注释解释
int t; // 温度值(单位:摄氏度)
int v; // 电压值(单位:毫伏)
// 正确做法:用好的命名
int temperature_celsius;
int voltage_mv;
我在项目中遇到过最夸张的例子:一个变量叫a1b2c3,注释写了五行来解释它的用途。我当时就想,你花五分钟想个好名字不行吗?
4.2.3 不要注释过时的或错误的逻辑
代码改了,注释没改,这是最坑人的。比没有注释还可怕。
// 等待100ms确保ADC稳定,但实际代码里是delay_ms(500)。后来排查问题时,我按注释的100ms去分析,死活找不到问题。最后发现是注释没更新。从那以后,我要求团队:改代码必须同步改注释,否则代码审查不通过。
4.2.4 不要用注释写「日记」
有些开发者喜欢在代码里写修改记录,比如// 2024-01-15 张三修改:修复bug。这些信息应该放在版本控制系统(Git、SVN)里,而不是代码里。
// 错误示范:把代码当日记本
// 2024-01-10 李四:初始版本
// 2024-01-12 王五:修改了延时参数
// 2024-01-15 张三:修复了溢出bug
void delay_ms(uint32_t ms) {
// ...
}
版本管理工具就是干这个的。代码里只保留当前有效的注释,历史记录交给Git去管。
4.3 避免过度注释的实用技巧
说了这么多,到底怎么把握度?我总结了几条经验。
4.3.1 三秒原则
看到一行代码,如果你能在三秒内理解它的意图,就不需要注释。如果超过三秒还没看懂,才考虑加注释。
4.3.2 注释密度控制
我一般遵循「每10-15行代码最多1行注释」的原则。当然这不是硬性规定,但可以作为参考。如果注释密度过高,说明代码本身可能有问题。
| 注释密度 | 评价 | 建议 |
|---|---|---|
| < 5% | 可能注释不足 | 检查是否有复杂逻辑未解释 |
| 5% - 15% | 合理范围 | 保持现状 |
| 15% - 30% | 注释偏多 | 考虑用更好的命名替代 |
| > 30% | 过度注释 | 需要重构代码 |
4.3.3 先写代码,后加注释
我个人的工作流是:先把功能实现出来,代码跑通了,再回头加注释。这样能避免「为了写注释而写注释」的冲动。而且,写完代码后你更清楚哪些地方真的需要解释。
4.4 总结:单行注释的黄金法则
一句话记住:注释应该回答「为什么」,而不是「是什么」。
三个不要:
- 不要注释显而易见的代码
- 不要用注释代替好的命名
- 不要写过时或错误的注释
三个要:
- 要解释复杂逻辑和非直观行为
- 要标记待办事项和已知问题
- 要合理分隔代码块
说白了,好的注释就像调味料——放对了,菜更香;放多了,菜就毁了。你想想看,你更愿意读一份「代码自解释、注释画龙点睛」的代码,还是一份「每行都有注释、读起来像老太太裹脚布」的代码?
嗯,答案不言自明。下次写//之前,先问自己一句:这行注释,真的有必要吗?