嵌入式 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 irqisr irq_handler, timer1_isr
Maximum max max_speed
Minimum min min_voltage
Current curcurrent 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 的基础,根据团队习惯微调即可。

posted @ 2026-09-14 17:51  一块一块儿  阅读(19)  评论(0)    收藏  举报