嵌入式 C 语言 snake_case 命名规范(完整版)
适用范围:嵌入式 C 项目,兼容 MISRA C:2012 标识符相关规则
第一章:基本规则
1.1 字符集
- 所有标识符只使用 小写字母
a-z、数字0-9、下划线_(宏/枚举常量除外) - 禁止使用驼峰或 PascalCase
- 下划线不得用于标识符开头或结尾(保留给编译器和标准库)
/* 正确 */
uint32_t motor_speed;
/* 错误 */
uint32_t MotorSpeed; /* 驼峰,禁止 */
uint32_t _motor_speed; /* 前导下划线,禁止 */
uint32_t motor_speed_; /* 尾部下划线,禁止 */
1.2 长度限制
| 场景 | 最小有效长度 | 说明 |
|---|---|---|
| 局部变量 | 31 个字符 | MISRA C:2012 Rule 5.1,C90 标准 |
| 外部标识符 | 31 个字符 | 同上 |
| 宏名 | 31 个字符 | 同上 |
实际上现代编译器通常支持 63 位甚至更长,但保持 31 位以内有意义是良好实践。
1.3 可读性原则
- 每个单词间用单个下划线分隔
- 禁止连续下划线:
speed__raw— 错误 - 名字应有明确含义,避免无意义缩写
/* 正确 — 清晰明了 */
uint32_t encoder_pulse_count;
/* 错误 — 含义模糊 */
uint32_t epc;
uint32_t enc_pls_cnt;
1.4 禁止仅靠大小写区分标识符
即使全部小写,也应避免仅靠下划线位置不同来区分标识符:
/* 危险 — 容易混淆 */
uint32_t motor_speed;
uint32_t motor_speed_raw;
uint32_t motor_speedlimit; /* 缺少分隔符,视觉上难以区分 */
第二章:变量命名
2.1 局部变量
结构:小写单词 + 下划线,名称直接描述变量的用途或含义。
void calculate_pid(void)
{
int32_t error = 0;
int32_t integral_sum = 0;
int32_t derivative = 0;
uint32_t current_tick = 0;
}
2.2 函数参数
与局部变量风格一致:
int16_t pid_calculate(int16_t setpoint, int16_t measured, uint16_t dt_ms)
{
int16_t error = setpoint - measured;
/* ... */
}
2.3 静态局部变量
不加前缀,和普通局部变量一样命名,但可在注释中标注其生命周期:
void sensor_update(void)
{
static uint32_t filter_buffer[8]; /* static 局部变量 */
static uint8_t buffer_index;
/* ... */
}
2.4 文件作用域静态变量(模块私有)
加 s_ 前缀,表明该变量属于本文件内部,不可被外部访问:
/* motor.c */
static uint32_t s_current_speed;
static bool s_is_running;
static int32_t s_encoder_position;
2.5 全局变量
加 g_ 前缀,表明该变量是全局可见的:
/* system.c */
volatile uint32_t g_system_tick;
volatile uint8_t g_error_code;
bool g_is_calibrated;
MISRA C:2012 Rule 8.9 建议将变量定义在最小作用域内。全局变量应尽量减少使用,但嵌入式项目中不可避免。
2.6 volatile 变量
volatile 本身不是命名问题,但嵌入式中经常出现,建议在语义上明确表达其硬件关联:
volatile uint32_t g_system_tick; /* 系统节拍 */
volatile uint8_t g_adc_conversion_done; /* ADC 完成标志 */
第三章:指针变量
3.1 指针前缀 p_
在变量名前加 p_ 前缀表示指针:
uint8_t *p_rx_buffer;
uint32_t *p_timer_value;
uart_config_t *p_config;
3.2 二维指针 / 指针的指针
uint8_t **pp_buffer;
3.3 函数指针
函数指针类型和变量都用 snake_case,fp_ 前缀表示函数指针变量:
/* 函数指针类型定义 */
typedef void (*timer_callback_t)(uint32_t tick);
/* 函数指针变量 */
timer_callback_t fp_timeout_handler;
3.4 const 指针
/* 指向 const 数据的指针 — 数据不可改 */
const uint8_t *p_rx_data;
/* const 指针 — 指针本身不可改 */
uint8_t *const p_tx_buffer = tx_buf;
/* 两者都是 const */
const uint8_t *const p_rom_data;
第四章:函数命名
4.1 结构
模块名_动作_对象,全部小写下划线:
<module>_<verb>_<object>
/* 模块:motor,动作:init / get / set / start / stop / enable / disable */
void motor_init(void);
uint32_t motor_get_speed(void);
void motor_set_speed(uint32_t speed_rpm);
void motor_start(void);
void motor_stop(void);
void motor_enable_brake(void);
/* 模块:sensor */
int16_t sensor_read_temperature(void);
uint16_t sensor_read_adc(uint8_t channel);
void sensor_calibrate(void);
4.2 常用动词
| 动词 | 含义 | 示例 |
|---|---|---|
init |
初始化 | motor_init() |
deinit |
反初始化 | uart_deinit() |
get |
获取值 | motor_get_speed() |
set |
设置值 | motor_set_speed() |
read |
从硬件读 | adc_read_value() |
write |
写入硬件 | eeprom_write_data() |
start |
启动过程 | timer_start() |
stop |
停止过程 | timer_stop() |
enable |
使能功能 | interrupt_enable() |
disable |
禁用功能 | interrupt_disable() |
is / has |
查询状态(返回 bool) | motor_is_running() |
handle |
处理事件 | can_handle_rx() |
callback |
回调函数 | timer_overflow_callback() |
convert |
转换 | adc_convert() |
calculate |
计算 | pid_calculate() |
send |
发送 | uart_send() |
receive |
接收 | uart_receive() |
4.3 布尔返回函数
以 is_、has_、can_ 开头,读起来像一个问题:
bool motor_is_running(void);
bool motor_has_fault(void);
bool uart_can_send(void);
bool buffer_is_full(const ring_buffer_t *p_buf);
4.4 回调函数
/* 定义回调类型 */
typedef void (*can_rx_callback_t)(uint32_t id, const uint8_t *p_data, uint8_t len);
/* 具体回调实现 */
void app_can_rx_callback(uint32_t id, const uint8_t *p_data, uint8_t len);
4.5 中断服务程序
以 _isr 或 _irq_handler 结尾:
void timer1_isr(void);
void uart1_rx_isr(void);
void exti0_irq_handler(void);
void dma1_channel1_isr(void);
第五章:类型定义
5.1 结构体
全小写 + _t 后缀,typedef 后使用:
typedef struct {
uint32_t baud_rate;
uint8_t data_bits;
uint8_t stop_bits;
uint8_t parity;
} uart_config_t;
typedef struct {
int32_t kp;
int32_t ki;
int32_t kd;
int32_t integral_limit;
int16_t output_min;
int16_t output_max;
} pid_param_t;
5.2 命名结构体类型
当结构体需要自身引用时,给 struct 标签也用 snake_case:
typedef struct node {
uint32_t data;
struct node *p_next;
} node_t;
5.3 联合体
typedef union {
uint32_t word;
struct {
uint32_t bit0 : 1;
uint32_t bit1 : 1;
/* ... */
} bits;
} reg_ctrl_t;
5.4 枚举类型
typedef enum {
STATE_IDLE,
STATE_INIT,
STATE_RUNNING,
STATE_STOPPED,
STATE_ERROR,
STATE_COUNT /* 用于数组大小,放在最后 */
} system_state_t;
typedef enum {
ERROR_NONE = 0,
ERROR_OVERCURRENT,
ERROR_OVERVOLTAGE,
ERROR_OVERTEMP,
ERROR_SENSOR_FAULT,
} error_code_t;
5.5 函数指针类型
typedef void (*isr_handler_t)(void);
typedef int16_t (*sensor_read_func_t)(uint8_t channel);
typedef bool (*filter_func_t)(uint16_t raw_value);
第六章:枚举常量
结构:全大写 + 下划线,与宏保持一致的视觉风格:
typedef enum {
GPIO_MODE_INPUT,
GPIO_MODE_OUTPUT,
GPIO_MODE_AF,
GPIO_MODE_ANALOG,
} gpio_mode_t;
typedef enum {
CLOCK_SRC_HSI,
CLOCK_SRC_HSE,
CLOCK_SRC_PLL,
} clock_source_t;
枚举常量全大写是唯一需要"切大小写"的地方,但枚举值通常集中定义在一个地方,写完之后在代码中只以全大写形式出现,不会造成频繁切换。
第七章:宏与预处理器
7.1 常量宏
全大写 + 下划线:
#define MAX_MOTOR_SPEED 3000
#define MIN_MOTOR_SPEED 0
#define ADC_RESOLUTION_BITS 12
#define ADC_MAX_VALUE 4095
#define UART_TX_BUFFER_SIZE 256
#define UART_RX_BUFFER_SIZE 256
#define SYSTEM_TICK_FREQUENCY 1000
#define PI_FIXED_POINT_16_16 205887 /* 3.14159 * 65536 */
7.2 功能宏(带参数)
同样全大写,参数名全小写:
#define BYTE_TO_KB(bytes) ((bytes) / 1024u)
#define CLAMP(val, lo, hi) ((val) < (lo) ? (lo) : ((val) > (hi) ? (hi) : (val)))
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#define BIT(n) (1u << (n))
/* 多行宏用 do-while(0) 包裹 */
#define ENTER_CRITICAL() do { __disable_irq(); } while (0)
#define EXIT_CRITICAL() do { __enable_irq(); } while (0)
7.3 条件编译
#define ENABLE_MOTOR_DEBUG 1
#define TARGET_HW_REVISION_B 1
#if ENABLE_MOTOR_DEBUG
/* debug 代码 */
#endif
7.4 头文件保护
使用 文件路径相关的全大写名称:
/* motor.h */
#ifndef MOTOR_H
#define MOTOR_H
/* ... */
#endif /* MOTOR_H */
如果有子目录,用下划线代替斜杠:
/* hal/stm32/uart.h */
#ifndef HAL_STM32_UART_H
#define HAL_STM32_UART_H
/* ... */
#endif /* HAL_STM32_UART_H */
第八章:变量前缀(类型暗示)
注意:MISRA 并不要求也不反对类型前缀。这是团队规范层面的选择。
如果团队希望在名字中携带类型信息,以下是一套适配 snake_case 的前缀方案:
8.1 基本类型前缀
| 前缀 | 类型 | 示例 |
|---|---|---|
u8_ |
uint8_t |
u8_rx_count |
u16_ |
uint16_t |
u16_adc_raw |
u32_ |
uint32_t |
u32_timestamp |
i8_ |
int8_t |
i8_temperature_offset |
i16_ |
int16_t |
i16_error_value |
i32_ |
int32_t |
i32_encoder_count |
b_ |
bool |
b_is_ready |
8.2 作用域前缀
| 前缀 | 含义 | 示例 |
|---|---|---|
g_ |
全局变量 | g_system_tick |
s_ |
文件静态变量 | s_current_speed |
p_ |
指针 | p_rx_buffer |
fp_ |
函数指针 | fp_callback |
8.3 前缀叠加顺序
作用域 → 类型 → 名称
static uint32_t s_u32_motor_speed; /* 文件静态 + uint32 */
volatile uint32_t g_u32_system_tick; /* 全局 + uint32 */
uint8_t *p_u8_tx_buffer; /* 指针 + uint8 */
是否使用类型前缀是团队选择。 如果你觉得
s_u32_motor_speed太啰嗦,直接写s_motor_speed完全没问题。现代 IDE 鼠标悬停就能看到类型,类型前缀的价值在下降。
第九章:文件命名
9.1 源文件和头文件
全小写 + 下划线,与模块名一致:
project/
├── src/
│ ├── main.c
│ ├── motor.c
│ ├── motor_ctrl.c
│ ├── sensor.c
│ ├── pid.c
│ ├── ring_buffer.c
│ └── system_init.c
├── include/
│ ├── motor.h
│ ├── motor_ctrl.h
│ ├── sensor.h
│ ├── pid.h
│ ├── ring_buffer.h
│ ├── system_init.h
│ └── common_types.h
└── hal/
├── uart.c
├── uart.h
├── gpio.c
└── gpio.h
9.2 一个模块一对文件
motor.c ← 实现
motor.h ← 接口
第十章:缩写规范
10.1 允许的通用缩写
嵌入式领域中广泛认知的缩写可以直接使用:
| 全称 | 缩写 | 示例 |
|---|---|---|
| Motor | motor |
motor_speed |
| Temperature | temp |
temp_sensor |
| Receive | rx |
uart_rx_buffer |
| Transmit | tx |
uart_tx_buffer |
| Interrupt | irq 或 isr |
irq_handler, timer1_isr |
| Maximum | max |
max_speed |
| Minimum | min |
min_voltage |
| Current | cur 或 current |
motor_current |
| Configuration | config |
uart_config_t |
| Buffer | buf |
tx_buf |
| Length | len |
data_len |
| Count | cnt |
error_cnt |
| Address | addr |
device_addr |
| Number | num |
channel_num |
| Register | reg |
ctrl_reg |
| Flag | flag |
data_ready_flag |
| Index | idx |
buffer_idx |
| Previous | prev |
prev_state |
10.2 缩写规则
- 缩写词在名字中保持全小写,不再是全大写
/* 正确 — 缩写融入小写风格 */
uint32_t uart_baud_rate;
uint8_t spi_rx_buf[64];
void irq_enable(void);
/* 错误 — 缩写不应跳出小写风格 */
uint32_t UART_baud_rate; /* 禁止 */
uint8_t SPI_RX_buf[64]; /* 禁止 */
例外:宏定义和枚举常量本身就是全大写,缩写自然大写,不存在冲突。
10.3 不建议缩写的
对于不广为人知的缩写,写出完整单词:
/* 不好 — 难以理解 */
uint32_t spd_ctrl_val;
/* 好 — 一目了然 */
uint32_t speed_control_value;
第十一章:MISRA 合规性检查清单
以下规则直接影响命名,项目中应重点关注:
| MISRA 规则 | 要求 | 命名规范中的对应 |
|---|---|---|
| Rule 2.1 | 不应有不可达代码 | 不影响命名 |
| Rule 5.1 | 外部标识符应彼此区分 | 模块前缀确保唯一性 |
| Rule 5.2 | 同一作用域标识符应区分于前 63 个字符 | 所有名字自然满足 |
| Rule 5.3 | 内部标识符不应遮蔽外部标识符 | 局部变量不要与全局同名 |
| Rule 5.4 | 宏标识符应可区分 | 宏全大写,与变量自然区分 |
| Rule 5.5 | 标识符不应在不同作用域复用 | 不同模块用不同前缀 |
| Directive 4.6 | 使用 <stdint.h> 类型 |
类型名统一用 uint32_t 等 |
| Rule 8.9 | 变量应在最小作用域定义 | 减少全局变量,优先局部 |
第十二章:速查表
| 类别 | 风格 | 示例 |
|---|---|---|
| 局部变量 | 全小写 + 下划线 | speed_raw, error_sum |
| 函数参数 | 全小写 + 下划线 | target_speed, dt_ms |
| 文件静态变量 | s_ + 全小写 | s_current_speed |
| 全局变量 | g_ + 全小写 | g_system_tick |
| 指针变量 | p_ + 全小写 | p_rx_buffer |
| 函数指针变量 | fp_ + 全小写 | fp_callback |
| 函数 | 模块_动作_对象 | motor_get_speed() |
| 布尔查询函数 | is_/has_/can_ + 描述 | motor_is_running() |
| ISR | 名称_isr / 名称_handler | timer1_isr() |
| 结构体类型 | 全小写 + _t | uart_config_t |
| 枚举类型 | 全小写 + _t | system_state_t |
| 枚举常量 | 全大写 + 下划线 | STATE_IDLE |
| 联合体类型 | 全小写 + _t | reg_ctrl_t |
| 函数指针类型 | 全小写 + _t | timer_callback_t |
| 常量宏 | 全大写 + 下划线 | MAX_BUFFER_SIZE |
| 功能宏 | 全大写 + 下划线 | CLAMP(val, lo, hi) |
| 头文件保护 | 全大写 + _H | MOTOR_H |
| 源文件 | 全小写 + 下划线 | motor_ctrl.c |
| 头文件 | 全小写 + 下划线 | motor_ctrl.h |
╔═══════════════════════╦═══════════════════════════════╦══════════════════════════════════════════╗
║ Category ║ Convention ║ Examples ║
╠═══════════════════════╬═══════════════════════════════╬══════════════════════════════════════════╣
║ Local variable ║ lowercase + underscores ║ speed_raw, error_sum ║
║ Function parameter ║ lowercase + underscores ║ target_speed, dt_ms ║
║ File-static variable ║ s_ + lowercase ║ s_current_speed ║
║ Global variable ║ g_ + lowercase ║ g_system_tick ║
║ Pointer variable ║ p_ + lowercase ║ p_rx_buffer ║
║ Function pointer var ║ fp_ + lowercase ║ fp_timeout_handler ║
║ Function ║ module_verb_object ║ motor_get_speed() ║
║ Boolean query func ║ is_/has_/can_ + description ║ motor_is_running() ║
║ Callback function ║ context_event_callback ║ app_can_rx_callback() ║
║ ISR ║ name_isr / name_irq_handler ║ timer1_isr(), exti0_irq_handler() ║
║ Struct type ║ lowercase + _t ║ uart_config_t ║
║ Enum type ║ lowercase + _t ║ system_state_t ║
║ Enum constant ║ UPPERCASE + underscores ║ STATE_IDLE, ERROR_OVERCURRENT ║
║ Union type ║ lowercase + _t ║ reg_ctrl_t ║
║ Function pointer type║ lowercase + _t ║ timer_callback_t ║
║ Object-like macro ║ UPPERCASE + underscores ║ MAX_BUFFER_SIZE ║
║ Function-like macro ║ UPPERCASE + underscores ║ CLAMP(val, lo, hi) ║
║ Header guard ║ UPPERCASE + _H ║ MOTOR_H, HAL_STM32_UART_H ║
║ Source file ║ lowercase + underscores ║ motor_ctrl.c ║
║ Header file ║ lowercase + underscores ║ motor_ctrl.h ║
╠═══════════════════════╩═══════════════════════════════╩══════════════════════════════════════════╣
║ Scope Prefix Order ║ scope → type → name ║ s_u32_motor_speed ║
║ Pointer Order ║ scope → p_ → name ║ g_p_tx_buffer ║
╠═══════════════════════╩═══════════════════════════════╩══════════════════════════════════════════╣
║ MISRA Key Rules ║
║ Rule 5.1 External identifiers shall be distinct ║
║ Rule 5.2 Identifiers in the same scope shall differ by >63 chars ║
║ Rule 5.3 An identifier shall not shadow an outer scope identifier ║
║ Rule 5.4 Macro identifiers shall be distinct from other macro identifiers ║
║ Rule 5.5 Identifiers shall not be reused across name spaces ║
║ Dir 4.6 Use fixed-width integer types from <stdint.h> ║
║ Rule 8.9 An object shall be defined at block scope when possible ║
╚══════════════════════════════════════════════════════════════════════════════════════════════════╝
这套规范覆盖了嵌入式 C 开发中你能遇到的所有命名场景。实际使用时建议将它作为项目 coding_style.md 的基础,根据团队习惯微调即可。

浙公网安备 33010602011771号