# Embedded C Coding Standard Zh V2

> 按嵌入式 C 语言编码规范编写、重构、审查和整理固件代码。用于 STM32、RTOS、BSP、HAL、驱动、服务层、中间件等场景下的 `.c` 文件与相关 `.h` 文件工作，特别适用于用户请求提到C语言编码规范、命名统一、注释补齐、大括号风格、返回值检查、内存安全、32/64位可移植性、静态检查、Git 变更增量审查或代码风格整改时。

- Skill: `ethan-hang/embedded-c-coding-standard-zh-v2` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add ethan-hang/embedded-c-coding-standard-zh-v2`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ethan-hang/embedded-c-coding-standard-zh-v2/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ethan-hang (https://skillmd.com/u/ethan-hang)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ethan-hang/embedded-c-coding-standard-zh-v2

---


# 嵌入式 C 语言编码规范 V2

按嵌入式规范处理项目自有嵌入式 C 代码，并尽量控制改动范围，避免产生无意义的大 diff。

## V2 默认工作模式

- 当任务是 `review`、`审查`、`整改`、`提交前检查`、`看本次改动有没有问题` 这类增量检查时，优先使用 Git 变更范围，而不是全工程扫描。
- 增量审查默认只锁定本次变更涉及的自研 `.c/.h` 文件，再结合函数级上下文、同模块头文件和直接关联定义做审查。
- 不要机械地只看 diff 行；要把 diff 当入口，再补读当前函数、必要的结构体、宏定义、枚举、资源释放路径和直接相关接口。
- 只有在用户明确要求全量扫描、建立初始基线、做大规模规范治理、批量重构或 CI 规则收敛时，才转为目录级或全工程审查。
- 若当前目录不是 Git 仓库，或仓库状态不足以确定基线，则退化为用户指定文件范围、目录范围或当前任务涉及文件的局部审查。

## 先判断审查范围

- 如果用户没有明确要求全量扫描，且当前工作区是 Git 仓库，优先通过 Git 确定增量范围。
- 优先审查以下几类范围：
- 工作区未提交变更
- 暂存区变更
- 当前分支相对基线分支的变更
- 只将变更集中的自研 `.c`、`.h` 文件纳入默认审查范围；生成代码、第三方代码和构建产物默认排除。
- 若用户说“只看这次提交”“只看这次改动”“只审变更文件”，默认就是增量审查，不再主动扩展到全仓。
- 若变更文件很少，但影响到公共头文件、宏定义、共享结构体或资源生命周期，可按需补读直接相关文件；不要无边界扩散。

## 先判断代码归属

- 先判断目标文件属于项目自研代码、自动生成代码，还是第三方代码。
<!-- - 项目自研代码目录（如应用层、服务层、平台层、BSP、工具库等自研目录）按项目实际结构识别，默认视为需按本规范处理的范围。 -->
- 默认将 `Drivers/`、`03_Os/`、外部中间件，以及 `Core/` 下大多数 STM32Cube 生成文件视为自动生成或上游代码。
- 若目标属于第三方或生成代码，除非用户明确要求统一风格，否则只修改必要逻辑，尽量保留原有布局和上游风格。
- 若规范与上游风格冲突，优先保持局部一致性，并在最终回复里说明这是例外处理。

## 先消解文档里的歧义

- 把原始规范文档视为“以 `.c` 实现文件为中心”的规范：所有 MUST 规则默认对 `.c` 生效；只有在确实修改接口时，才把命名、注释、宏和接口相关规则同步应用到 `.h`。
- 当原文摘要表、正文和示例互相冲突时，优先级按以下顺序处理：
- 安全性和正确性规则
- 正文详细说明与具体示例
- 仓库内稳定的既有约定
- 摘要表中的简写结论
- 在项目自有代码中新建标识符时，默认采用以下归一命名规则：
- 文件名与函数名：`snake_case`
- 局部变量与参数：`lowerCamelCase`
- 文件级或全局可变状态：`g_` + `lowerCamelCase`
- 宏、枚举值、标签：`UPPER_SNAKE_CASE`
- `typedef` 后的抽象类型名：`PascalCase`
- 不要为了追求风格统一去大面积重命名稳定公共 API、生成代码符号、厂商接口或已有调用链，除非用户明确要求。

## 应用格式与文件组织规则

- 只使用空格缩进，每一级 4 个空格，禁止插入 TAB。
- 行宽不超过 81 列；URL、命令行、`#include`、`#error` 等确有必要时可例外。
- 使用 K&R 大括号风格：
- 函数的左大括号独占一行
- 控制语句的左大括号放在语句行末
- 右大括号通常独占一行，除非后面紧跟 `else`、`else if` 或 `do { } while (0)` 的尾部 `while`
- `if`、`else`、`for`、`while`、`do`、`switch` 一律显式使用大括号，即使只有一条语句或空循环体。
- `switch` 中的 `case` 和 `default` 相对 `switch` 缩进一级，并显式写出 `break`、`return` 或说明型 fallthrough。
- 保持一行一条语句，不要把多条动作塞进一行，也不要用晦涩写法隐藏副作用。
- 若项目使用 CMake 或 Makefile 构建，头文件搜索路径应按“底层到顶层”排序，以减少无效路径匹配并改善编译速度。推荐顺序：芯片与架构层（CMSIS、设备头）→ HAL/驱动层 → OS 与第三方中间件 → BSP/平台组件 → 应用层。
- 新建或大幅重写 `.c` 文件时，优先采用以下组织顺序：
- 文件头注释
- `#include`
- 宏与常量
- 类型定义
- 文件级 `static` 变量
- 内部声明
- 内部 `static` 函数
- 对外函数
- 新建 `.c` 或 `.h` 文件时必须使用以下文件头注释模板（替换对应字段）：
  ```c
  /******************************************************************************
   * @file <filename>
   *
   * @par dependencies
   *
   * @author <Author Name>
   *
   * @brief <Brief description>
   *
   * @version V1.0 <YYYY-M-D>
   *
   * @note 1 tab == 4 spaces!
   *****************************************************************************/
  ```
- 所有函数声明，和函数定义需要包含注释，必须使用以下注释模板：
  ```c
  /**
  * @brief
  * 
  * @param[in] : 
  * 
  * @param[out] : 
  * 
  * @return
  * 
  * */
  ```
- 函数内部注释，需要遵循下面意见，注释统一写在代码前一行，必须使用以下注释模板：
  /**
  * This is a comment
  **/
- 注释默认用英文，重点说明设计意图、边界条件、硬件约束和非显然取舍，不要机械复述代码本身,每个函数都必须要有函数内注释。

## 应用安全性、可维护性与可移植性规则

- 让每个函数尽量只做一件事。可行时将函数控制在 60 行非空非注释代码以内，嵌套层级不超过 4 层。
- 对任何可能失败的 API，调用后立即检查返回值，包括项目接口、HAL、OS、内存分配、I/O 等。
- 同一模块内部尽量保持一致的返回值策略，不要混用多套错误语义。
- 对所有外部输入做显式校验，包括 API 入参、文件数据、网络报文、IPC 数据、环境信息，以及跨信任边界的共享全局状态。
- 不要用 `assert` 校验外部输入；`assert` 仅用于内部不变量。
- 对动态内存、锁、句柄、队列、信号量等资源，确保所有退出路径都能正确释放；资源较多时优先使用 `cleanup:` 收口。
- 优先使用函数或 `static inline` 替代函数式宏；若必须使用宏，必须完整加括号，多语句宏必须写成 `do { ... } while (0)`。
- 尽量减少全局状态；如果符号只在当前文件使用，就声明为 `static`。对文件级或全局可变状态使用 `g_` 前缀。
- 若文件级可变状态可能被多线程或中断上下文访问，必须明确同步策略，如互斥锁、原子、关中断或单线程约束。
- 指针运算优先使用 `uintptr_t` 或字节指针；不要通过 `uint32_t` 传递或截断指针。
- 长度、大小和索引优先使用 `size_t`；若必须缩窄到 32 位，先做显式边界检查。
- 生产代码优先走项目统一日志接口，不要随意保留 `printf`；同时避免打印敏感数据。
- 调试钩子、临时日志和测试代码不要混入发布路径；如必须保留，使用编译开关保护。

## 审查与整改时的优先级

- 先处理正确性、安全性、资源生命周期、并发和可移植性问题，再处理纯风格问题。
- 做增量审查时，先判断本次修改是否引入新的安全性或正确性风险，再考虑是否需要顺手规范化局部旧代码。
- 重点关注以下问题：
- 未检查返回值
- 未校验外部长度或范围
- 宏副作用与括号缺失
- 控制语句缺少大括号
- 指针截断
- `size_t` 缩窄
- 内存泄漏、重复释放、释放非法对象
- 本应是 `static` 的新增全局状态
- 除非用户明确要求，否则不要为了“看起来更整齐”而整文件重排或全量格式化。

## V2 Git 增量审查指引

- 在 Git 仓库中，优先使用以下思路确定审查范围：
- 工作区未提交：`git status --short` + `git diff --name-only`
- 暂存区：`git diff --cached --name-only`
- 分支差异：`git diff --name-only <base>...HEAD`
- 变更过滤时优先保留项目自研模块（按项目实际目录结构识别，如应用层、服务层、平台层、BSP、工具库等自研目录）。
- 默认排除：
- `Drivers/`
- `03_Os/`
- 外部中间件
- `Core/` 下大多数生成代码
- 构建产物、日志和临时文件
- 审查输出要聚焦本次变更引入的问题；历史遗留问题可简短提示，但不要让它们淹没本次改动的 findings。
- 若用户明确要求“顺带给出历史问题”或“建立基线”，再额外列出非本次改动问题。
- 若 Git 不可用，明确说明已退化为局部范围审查，并说明当前使用的范围选择依据。

## 需要更详细规则时再加载参考文档

- 做大规模 review、制定 CI 门禁、补 `.clang-format`、`.clang-tidy`、`cppcheck` 规则时，再读取 `references/embedded-c-rules-zh.md`。
- 需要把 Git 增量审查流程讲清楚、给出范围选择示例或说明为何不做全仓扫描时，再读取 `references/git-incremental-review-zh.md`。
- 构建静态检查配置时，优先从参考文档中的示例派生，不要随意发明与嵌入式规范无关的新规则。

