代码规范与风格:NuttX编码风格与Checkpatch使用
聊到代码规范,我猜你可能会觉得:「不就是缩进和命名嘛,能有多大事?」
嗯,我以前也这么想。直到有一次,我提交了一个自认为完美的补丁,结果被社区reviewer打回来三次——第一次说命名不对,第二次说缩进错了,第三次说注释风格不统一。从那以后,我老老实实把编码规范打印出来贴在工位上。
说白了,NuttX的编码风格不是摆设。它是社区协作的「通用语言」。你写出来的代码,别人要能一眼看懂、快速review、放心合并。今天我们就来把这套「通用语言」彻底搞明白。
命名规范:见名知意
我个人习惯是:变量名要能「自解释」。你想想看,三个月后你自己回来看代码,如果还要猜这个变量是干嘛的,那命名就失败了。
变量命名
- 局部变量:小写字母 + 下划线。比如
int ret;、int fd;、uint32_t reg_val; - 全局变量:加
g_前缀。比如int g_system_tick; - 静态变量:加
s_前缀。比如static int s_init_done; - 结构体成员:小写 + 下划线,不要用驼峰。比如
struct task_info_s { int pid; char name[32]; };
核心原则:NuttX 不使用驼峰命名法。所有标识符都是小写字母 + 下划线。这是和 Linux 内核一脉相承的风格。
函数命名
- 模块前缀:每个子系统有自己的前缀。比如
up_开头的是架构相关函数(up_irq_save),nx_开头的是图形相关函数。 - 动词 + 名词:比如
task_create、sem_wait、file_open。 - 内部函数:加
_前缀表示内部使用。比如_do_irq_handler。
我的小技巧:写函数名之前,先想清楚这个函数「做什么」,然后用一个动词开头。如果找不到合适的动词,说明函数职责可能不单一。
缩进与格式:强迫症患者的福音
NuttX 的缩进规则非常严格。我刚开始贡献时,光缩进就被怼了好几次。后来我学乖了——直接用工具检查。
缩进规则
- 缩进用 Tab,宽度 8 个字符。没错,就是 8 个空格宽度的 Tab。别问为什么,问就是传统。
- 续行用 8 个空格对齐。如果一行太长要换行,用 8 个空格缩进,而不是 Tab。
- 花括号位置:函数定义的花括号另起一行,控制语句的花括号跟在行尾。
/* 正确的花括号风格 */
int function(int a, int b)
{
if (a > b) {
return a;
} else {
return b;
}
}
/* 错误的风格 */
int function(int a, int b) {
if (a > b)
{
return a;
}
}
行长度与空格
- 每行不超过 80 个字符。这是老传统了,但确实能让代码在终端里好看。
- 运算符前后加空格:
a + b而不是a+b。 - 逗号后面加空格:
func(a, b, c)而不是func(a,b,c)。 - 关键字后面加空格:
if (x)、while (y)、for (i = 0; i < n; i++)。
我曾经踩过的坑:在结构体初始化时,我习惯把 = { 写在同一行。但 NuttX 要求 = 后面换行,然后缩进初始化列表。这个细节让我改了三遍才通过。
注释风格:不多不少刚刚好
注释这件事,我见过两个极端:一种是不写注释,另一种是每行都写注释。我个人觉得,注释要「解释为什么」,而不是「描述是什么」。
文件头注释
每个源文件开头都要有标准的注释块,包含版权信息和简要描述:
/****************************************************************************
* arch/arm/src/stm32/stm32_gpio.c
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to you under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
****************************************************************************/
函数注释
每个导出函数都要有注释,说明功能、参数和返回值:
/****************************************************************************
* Name: stm32_gpio_write
*
* Description:
* Write one or zero to the selected GPIO pin.
*
* Input Parameters:
* pinset - GPIO pin configuration
* value - 0 or 1
*
* Returned Value:
* OK on success; ERROR on failure
*
****************************************************************************/
int stm32_gpio_write(uint32_t pinset, bool value)
{
...
}
行内注释
- 用
/* ... */风格,不要用//。 - 放在代码上方或右侧,不要放在代码下方。
- 解释「为什么这么做」,而不是「这行代码做了什么」。
/* 好的注释:解释原因 */
ret = sem_wait(&sem); /* 等待硬件就绪,超时由调用者处理 */
/* 不好的注释:描述事实 */
ret = sem_wait(&sem); /* 调用 sem_wait */
Checkpatch:你的代码审查机器人
说实话,手动检查编码风格太累了。NuttX 社区提供了一个工具——checkpatch.sh,它就像你的代码审查机器人。
基本用法
# 检查单个文件
./tools/checkpatch.sh -f path/to/your/file.c
# 检查补丁文件
./tools/checkpatch.sh -p path/to/your/patch.patch
# 检查整个目录
./tools/checkpatch.sh -d path/to/your/directory/
常见错误与修复
| 错误类型 | 错误示例 | 正确写法 |
|---|---|---|
| 行尾空格 | int a;␣ |
int a; |
| Tab 缩进错误 | ␣␣␣␣int a; |
→int a; |
| 超过 80 字符 | long_function_name(very_long_parameter1, very_long_parameter2); |
换行对齐 |
| 花括号位置 | if (x) { |
if (x) {(正确) |
| 缺少空格 | if(x) |
if (x) |
我的工作流:每次提交前,先跑一遍 checkpatch。如果报错太多,我会用 --fix 参数自动修复一部分问题,然后手动检查剩下的。这样能省下不少时间。
集成到 Git Hook
我个人建议把 checkpatch 集成到 pre-commit hook 里,这样每次提交都会自动检查:
#!/bin/bash
# .git/hooks/pre-commit
./tools/checkpatch.sh -g HEAD
if [ $? -ne 0 ]; then
echo "代码风格检查未通过,请修复后重新提交"
exit 1
fi
避坑指南:我踩过的那些坑
最后分享几个我实际遇到过的坑,希望能帮你少走弯路:
- 不要混用 Tab 和空格。编辑器设置里把 Tab 自动转空格关掉,NuttX 要求用真正的 Tab 字符。
- 空行不要有空格。空行就是空行,不要在里面藏空格。checkpatch 会报
trailing whitespace。 - 宏定义注意反斜杠对齐。多行宏的反斜杠要对齐在同一个列上,通常在第 80 列。
- switch-case 的缩进:case 和 switch 对齐,case 内部的代码再缩进一层。
/* 正确的 switch-case 缩进 */
switch (cmd) {
case CMD_START:
start_engine();
break;
case CMD_STOP:
stop_engine();
break;
default:
break;
}
嗯,编码规范这部分内容就这些。你可能会觉得琐碎,但相信我——当你的补丁被社区快速合并时,你会感谢自己花时间把这些细节搞清楚了。下一章我们聊聊如何搭建 NuttX 的开发环境,到时候见。