# C99 Standard C

> 编写、修改和评审 ISO C99 嵌入式固件模块、头文件、驱动、BSP、协议、状态机和安全相关 C 代码。用于 `.c/.h` 生成、文件归属判断、命名与格式统一、函数和内部逻辑注释、修改记录、实际编码保持、内存安全、ISR/并发、寄存器访问、32/64 位可移植性、可维护性整改，以及将可机器检查的 C 规范落实为 CLI/Hook/Lint 阻塞门禁。任何 C 语言编码任务都必须使用；默认中文输出。

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

---


# 嵌入式 C99 标准 C

默认使用中文回答。仅当项目接口、既有注释或用户明确要求英文时切换语言。

## Skill 类型

本 Skill 属于 `consistent-workflow`、`organization-standard` 和 `team-expertise`：它把 C99 嵌入式编码流程、团队编码标准和高级固件经验固化为可复用规则。

使用本 Skill 当：任务涉及重复 C 编码/评审流程、团队 C 规范、嵌入式安全/实时专业知识，或需要把 C 规则落成 CLI/Hook/Lint 阻塞门禁。

跳过本 Skill 当：任务不是 C 语言编码或评审、只是一次性简单解释、只是探索方案且不产生可复用规范。

## 工作流契约

触发：任何 `.c/.h` 编写、修改、评审、规范制定或 C 规则门禁实现任务。

输入：目标文件、文件归属、实际编码、项目阶段、现有构建/静态检查工具、用户要求和硬件/实时约束。

步骤：先判断文件归属和编码，再梳理接口/实现/副作用，执行最小修改，最后把可机器检查的规则落实为 CLI、Hook、Lint 或 CI 门禁。

完成标准：代码修改符合 C99/项目规范，必要注释和修改记录齐全，可机器检查规则说明了命令入口和阻塞阶段。

验证：编译、静态检查、边界条件、ISR/并发检查和 `quick_validate.py`/项目已有 hook 通过。

阻塞 Hook：新增或强化 C 规范时，若可稳定机器检查但没有 CLI/Hook/Lint/CI 阻塞方案，必须在输出中标为未完成风险，不得宣称规范已落地。

## 门禁规则

- CLI 检查器：优先使用项目已有编译命令、warning-as-error、`clang-tidy`、`cppcheck`、MISRA 工具或自定义脚本；检查输入为本次涉及的 `.c/.h`、公共头文件和构建配置，失败必须返回非零退出码并指明违规文件和规则。
- Hook 门禁：可机器检查的 C 规范应接入 pre-commit、Git hook、CI required check、发布打包或产线包生成；实时路径、ISR、公共头文件和编码转换类风险不能只靠口头审查。
- MCP 工具库：若存在项目诊断 MCP、构建 MCP、静态分析 MCP 或 EtherCAT/嵌入式项目诊断工具，优先作为信息来源；没有 MCP 时用 CLI/构建日志/人工审查替代并说明原因。
- 人工确认：由用户或项目负责人确认哪些规则已阻塞、哪些暂不阻塞、哪些需要人工审查。
- 暂不实现原因：无法稳定自动判断、项目缺工具、会误伤自动生成/第三方代码、或需要团队先确认编码/命名基线时，必须写明人工替代项。

## 人工反馈确认

- Owner/负责人：默认由当前用户确认；若项目已有代码负责人、架构负责人或质量负责人，优先记录实际确认人。
- Accepted gates/已接受门禁：列出本次采用的 CLI、Hook、CI、MCP 或人工审查项。
- Manual review/人工审查范围：记录无法自动判断的文件归属、硬件副作用、实时约束、修改记录真实性和生成代码边界。
- Explicit decision/明确决定：确认通过、带风险通过或退回继续补规则；没有确认时不得声称标准已完全落地。

## 规则优先级

按以下顺序裁决冲突：

1. 正确性、安全性、硬件和实时约束；
2. 用户对当前任务的明确要求；
3. 文件归属对应的规则；
4. 本 Skill 的通用编码规则；
5. 纯格式偏好。

只修改请求直接涉及的内容。禁止借风格整改之名扩大重构范围。

## 第一步：判断文件归属

修改前必须把目标文件归为以下一类，并在无法从仓库证据确定时询问用户：

- **项目自研代码**：由当前团队维护，公共接口和实现可按项目规范演进；
- **自动生成代码**：由 STM32Cube、配置器、IDL、代码生成器或构建工具产生；
- **第三方代码**：厂商 SDK、开源库、外部中间件或上游镜像。

不要仅根据目录名下结论。结合文件头、生成标记、许可证、构建脚本、包来源和仓库历史判断。

### 自动生成代码和第三方代码

- 默认保持文件实际编码、换行、命名、缩进、大括号和注释风格；
- 只修改需求或缺陷闭环必需的代码；
- 不批量格式化、不改公共符号、不补全无关注释；
- 需要偏离上游风格或修改生成区时，先说明再生覆盖和后续合并风险。

### 新项目和项目自研代码

新增或修改函数时应用固定规则：

- 使用 4 个空格缩进，禁止 TAB，行宽默认不超过 120 列；
- 文件名和函数名使用 `snake_case`；
- 局部变量和参数使用 `lowerCamelCase`；
- 文件级或全局可变状态使用 `g_` + `lowerCamelCase`，并尽可能声明为 `static`；
- 宏、枚举值和标签使用 `UPPER_SNAKE_CASE`；
- `typedef` 抽象类型使用 `PascalCase`；
- 函数左大括号独占一行；控制语句左大括号放在语句行末；
- `if`、`else`、`for`、`while`、`do` 和 `switch` 必须显式使用大括号；
- `case/default` 相对 `switch` 缩进一级，并显式 `break`、`return` 或说明 fallthrough。

不要仅为统一风格大面积重命名稳定公共 API；涉及调用链和 ABI 时先确认影响范围。

## 函数命名、入参与设计

以下规则适用于项目自研的新函数和本次确实需要修改的函数。稳定公共 API、标准规定符号、自动生成代码和第三方接口默认保持原名，不因本节规则主动重命名。

### 函数命名

- 函数名使用 `snake_case`。公共函数默认采用 `<module>_<verb>[_<object>]`，文件私有 `static` 函数可采用 `<verb>[_<object>]`；名称必须让首次阅读者能判断模块、动作和对象。
- 新函数名不得超过 32 个字符，默认不得超过两个下划线，每个下划线分隔词不得超过 12 个字符。多词模块需要动作和对象时，使用项目统一登记的短前缀，例如 `motor_control` 使用 `mc_set_target`，不得生成 `motor_control_set_target`；缩写必须稳定、无歧义且在同一模块内一致。
- 优先使用 `init/cfg/get/set/read/write/check/clear/reset/start/stop/open/close/update/send/recv/parse/create/delete/save/load` 等常用动作。`finalize`、`commit`、`retrieve` 和 `instantiate` 不得用于新函数名；`manifest` 可作为 OTA、Bootloader 等领域中的正式名词使用。
- `get` 表示取得模块已有值，`read` 表示主动读取硬件、存储或数据源，`peek` 表示查看但不消费；`set` 表示设置属性，`write` 表示写入硬件、存储或传输目标，`update` 表示按当前输入推进或重算状态。
- 布尔查询使用 `is/has/can`；`process/handle/control/service/manager` 等模糊词不得单独充当动作，只有与明确模块或对象组合且不能使用更准确动作时才允许。

### 函数入参

- 参数和局部变量使用 `lowerCamelCase`。参数按“实例/上下文、必需输入、输出”排列；缓冲区与其长度或容量必须相邻，并作为一个参数组放在对应输入或输出位置。
- 不修改调用者数据的指针使用 `const`；数组、缓冲区和字符串必须携带 `size_t` 长度或容量；时间、频率、地址单位无法从类型确定时写入参数名，例如 `timeoutMs`。
- 小型标量按值传递；状态、较大对象和多实例模块通过明确的上下文指针传递。指针的可空性、所有权、有效期、是否会被函数保留以及输出何时有效必须由接口注释说明。
- 输出参数放在输入参数之后，并且仅在函数返回成功后有效；失败路径不得留下调用者可能误用的半更新输出。
- 避免含义不明的布尔开关，优先使用枚举。参数超过 5 个时触发接口设计审查，但不得为满足数量指标机械地塞入结构体；只有具有共同语义、生命周期和校验规则的稳定参数组才定义配置或请求结构体。

### 函数设计

- 一个函数只承担一个可观察职责和一个主要变化原因。校验、纯计算、硬件 I/O、状态迁移和调度应在有独立语义或测试边界时拆分，不得为了满足行数指标制造只转发一次的碎片函数。
- 50 行非空非注释代码、4 层嵌套、5 个参数和非调度函数超过 5 个直接调用均为设计审查信号，不是自动判错条件；结合职责、时序、栈占用和可测试性决定是否拆分。
- 无失败语义的纯查询可直接返回值；可能失败的函数返回统一错误枚举，并通过输出参数返回结果。`bool` 只表达真正的真假问题，不得同时编码错误状态。
- 函数依赖和副作用必须显式：优先通过参数和返回值传递数据，避免隐藏全局状态；明确阻塞性、可重入性、ISR/任务上下文、共享状态保护和硬件副作用。
- 回调接口必须明确谁调用、何时调用以及运行于 ISR、任务还是主循环。通用回调默认采用“上下文指针 + 类型化事件 + 可选只读事件数据”；注册对象和上下文必须覆盖整个注册期，回调不得依赖临时调用环境或隐藏全局状态。
- ISR 或调用上下文不确定的回调必须非阻塞，不得动态分配、等待锁或执行复杂日志/协议处理；多实例模块必须为每个注册实例传递独立上下文。

## 编码规则

读取或写回前必须识别文件实际编码，不得先按 GBK 或 UTF-8 猜测解码。

- 自动生成代码和第三方代码：优先保持实际编码，不主动询问转换；
- 项目自研代码：若实际编码是 GBK，保持 GBK；
- 项目自研代码：若实际编码不是 GBK，必须询问用户是否转换，得到答复前保持实际编码；
- 新项目且尚无编码基线：询问用户确定编码，再创建包含非 ASCII 内容的源文件；
- 编码转换、BOM 变化和换行符转换必须单独列入 diff 与验证项。

## 注释和修改记录

项目自研代码中的所有函数必须有函数头注释，函数内部必须有说明设计意图、边界、步骤或硬件约束的注释。禁止用注释机械翻译每行代码。

新增或大幅修改函数时必须补充修改记录，包含：

- 作者（默认 `HU JIAXUAN`，项目已有负责人、作者或文件头规则时按项目规范替换）；
- `YYYY-MM-DD` 日期；
- 修改原因；
- 必要时记录问题现象、根因和修复方式。

作者无法从用户、仓库配置或既有记录确定时必须询问，不得虚构。新文件沿用项目现有文件头，不强制写固定年份、公司、作者或版权；项目没有模板时先询问。

具体模板和审查项读取 `references/c-coding-rules-zh.md`。

## C99 实现规则

- 使用 ISO C99、`stdint.h`、`stdbool.h`、`stddef.h` 和显式位宽类型；
- 长度、大小和索引优先使用 `size_t`，缩窄前检查范围；
- 指针到整数的转换使用 `uintptr_t`，禁止用 `uint32_t` 承载指针；
- 公共头文件必须自包含且只放声明，不定义公共变量或暴露私有实现；
- 文件私有函数和状态使用 `static`；
- 公共接口检查指针、长度、枚举和范围；所有外部输入默认不可信；
- 可能失败的 API 必须检查返回值，且在确认成功前不得使用结果；
- 禁止在实时路径使用递归、VLA、默认动态分配、无界循环和阻塞调用；
- 共享状态必须说明任务、中断、DMA、cache、原子性和临界区边界；
- 寄存器访问使用明确掩码并说明读改写、清标志顺序和副作用；
- 多语句宏使用 `do { ... } while (0)`，优先使用函数或 `static inline`。

## 可机器检查规则和阻塞门禁

当某条 C 规范能被稳定机器检查时，不能只写成文档或口头建议；必须优先落成 CLI、Hook、Lint、编译告警或 CI required check，并让失败明确阻塞提交、合并、发布或产线包生成。

优先机器化以下规则：

- 格式和命名：TAB、行宽、大括号、`case/default`、函数名长度/下划线/词长、公共符号命名和文件归属边界；
- C99 安全规则：VLA、递归、默认动态分配、未检查返回值、指针截断、公共头文件非自包含；
- 嵌入式实时规则：ISR/强实时路径中的阻塞调用、无界循环、禁止 API、临界区和共享状态约束；
- 寄存器和并发规则：读改写顺序、清标志副作用、DMA/cache 边界和 volatile/atomic 使用约束；
- 修改记录和注释规则：新增/大幅修改函数缺失函数头注释、作者、日期或修改原因。

函数名形态和原型参数数量可作为后续 parser/lint 候选；模块缩写是否清晰、动作词是否准确、参数是否内聚、职责是否单一、回调上下文和生命周期是否正确必须人工审查。项目尚无对应检查器时，明确记录为人工门禁，不得声称已由 CLI/Hook 自动阻塞。本 Skill 不要求为单次代码修改临时新增低可靠性的正则检查器。

实现形式按项目现有工具优先选择：编译器 warning-as-error、`clang-tidy`、`cppcheck`、MISRA 工具、`pre-commit`、Git hook、构建脚本、自定义 Python/PowerShell/Ruby CLI 或 CI job。规则脚本必须有清晰的非零退出码、可读错误信息和最小复现输入；不能稳定自动判断的规则才保留为人工审查清单。

新增或修改规范时，同步说明：

1. 是否已机器化；
2. 使用的命令或 hook 入口；
3. 阻塞的阶段：本地提交、CI 合并、发布打包或产线烧录；
4. 暂不能机器化的原因和人工审查替代项。

## 工作流

1. 识别文件归属、实际编码、工具生成边界和现有风格；
2. 先读公共头文件，再梳理实现、调用关系、状态所有权和硬件副作用；
3. 明确成功标准，只做最小完整修改；
4. 对项目自研代码应用固定命名、入参契约、函数设计、格式、函数注释和修改记录规则；
5. 清理本次修改产生的未使用符号，不处理既有无关问题；
6. 验证编译告警、静态检查、边界条件、ISR/并发和必要的 HIL 项。

需要函数命名、入参、回调和设计示例时读取 `references/c-coding-rules-zh.md`。需要其他历史模板或检查清单时再读取 `references/legacy-embedded-c99-skill.md`；规则优先级为本文件、`c-coding-rules-zh.md`、历史参考。

