代码规范与风格: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_createsem_waitfile_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 的开发环境,到时候见。