大厂嵌入式代码规范(二):命名规范与数据结构

文章摘要

  本篇文章聚焦嵌入式C代码的命名规范与变量设计,讲解大厂常用变量命名原则、全局与局部变量设计方法,以及常用缩写(cfg/init/cnt等)和成对词汇(init/deinit、start/stop、get/set)的最佳实践。通过规范化命名和变量设计,帮助开发者提升代码可读性、可维护性和团队协作效率,让你的嵌入式工程化代码更专业、更易维护。

文章目录

本栏系列文章

大厂嵌入式代码规范(一):文件结构与头文件设计
大厂嵌入式代码规范(三):程序版式
大厂嵌入式代码规范(四):语句版式与运算习惯
大厂嵌入式代码规范(五):函数设计与工程实践spm=1001.2014.3001.5501)

其他专栏精品文章

单片机开发环境搭建看这里
CSDN高赞项目:工程训练竞赛之垃圾分类垃圾桶
SLAMCraft:自主导航机器人DIY(一)如何设计一个自己的SLAM机器人

前言

  很多嵌入式程序员写的代码一眼就像"临时作业":变量名含糊、全局乱用、标志位不统一。

c 复制代码
int a;
int temp;
int flag1;

  在大厂,这样的代码几乎不会过Code Review。本篇,我们将从实战角度,系统讲解:

  • 变量命名规范

  • 全局与局部变量设计原则

  • flag、cfg、cnt 等常用缩写

  • struct / enum / union / bitfield 设计

  • typedef 重命名基础类型

  • 枚举显式赋值和位域设计

  • 跨平台类型长度问题和工程实践

  • 成对词汇使用(init/deinit、start/stop、get/set)

让你的代码既清晰,又可维护,写出来有工程感。

一、命名规范核心原则

  在嵌入式软件开发过程中,统一且清晰的命名规则可以显著提升代码的可读性和可维护性。变量命名方式有多种,这里建议采用如下命名方式:变量名中的单词全部使用小写 、多个具有完整含义的单词之间使用下划线 _ 连接。示例如下:

复制代码
channel_enable_status
channel_state

  如果变量中涉及关键含义的单词,而按照普通规则组合会导致变量名过长(例如超过两个单词),可以将关键字移动到变量名前面,以便开发人员更快理解变量含义。示例如下:

具体含义 修改前 修改后
功率设备配置 power_device_output_cfg cfg_power_device_output
系统设备初始化标志位 system_device_init_flag init_system_device_flag

  为了避免全局变量与局部变量出现重名,建议:局部变量使用名词或名词+动词的组合 、全局变量使用动词或动词+名词的组合。示例如下:

复制代码
system_init_flag
device_start_flag

  对于标志位类型变量,建议在变量名后添加 _flag 或 _flg 作为标识。示例如下:

c 复制代码
system_ready_flag
power_enable_flg

  此外,应避免以下不规范的命名方式:仅通过大小写区分变量 、仅通过数字后缀区分变量。示例如下:

c 复制代码
uint8 temp;
uint8 Temp;        // 不推荐,仅大小写区分

uint16 converse_flag1;
uint16 converse_flag2;   // 不推荐,仅数字区分

二、组合数据结构命名规范

  在嵌入式开发中,组合型数据结构通常包括 结构体(struct) 、枚举(enum) 、位域(bitfield) 、联合体(union) 等。为了让代码语义更加清晰,这类数据结构建议采用统一命名方式:语义 + 数据结构类型。

2.1 枚举类型规范

复制代码
typedef struct
{
    uint8  motor_id;          /* 电机编号 */
    uint8  motor_state;       /* 电机状态 */
    uint16 speed_rpm;         /* 电机转速,单位rpm */
    uint16 current_ma;        /* 电机电流,单位mA */
    uint32 run_time_s;        /* 电机累计运行时间,单位s */

} MOTOR_STATUS_STRUCT;

  在该示例中,MOTOR_STATUS_STRUCT 表示"电机状态结构体",成员变量统一采用 小写字母 + 下划线 的命名方式,并配有简洁明确的注释,能够清晰表达每个字段的含义。

2.2 枚举类型规范

  在组合型数据结构中,枚举类型建议进行显式赋值。这样可以提高代码可读性,并避免默认递增值带来的潜在错误。示例如下:

复制代码
typedef enum
{
    MOTOR_MODE_STOP  = 0x00,  /* 停止模式 */
    MOTOR_MODE_READY = 0x01,  /* 就绪模式 */
    MOTOR_MODE_RUN   = 0x02,  /* 运行模式 */
    MOTOR_MODE_FAULT = 0x03   /* 故障模式 */

} MOTOR_MODE_ENUM;

  在该示例中,MOTOR_MODE_ENUM 表示"电机模式枚举类型",每个枚举值都以 MOTOR_MODE_ 作为前缀,既避免命名冲突,也能在阅读代码时快速看出其所属语义范围。在实际开发中,适当使用 枚举类型替代宏定义,可以将一组相关常量集中管理,使代码结构更加清晰,也更便于调试和维护。

2.3 位域类型命名示例

  位域通常适用于资源受限场景下的状态压缩存储,例如设备故障标志、通信状态标志、诊断结果标志等。通过位域可以让每一位都具备明确的物理意义,同时减少存储空间占用。在定义位域时,建议注意以下几点:未使用位统一命名为 reserved 、位域总长度不能超过基础类型位宽 、每一位都应配备注释说明。示例如下:

复制代码
typedef union
{
    struct
    {
        uint16 over_voltage_err   : 1;  /* 过压故障 */
        uint16 under_voltage_err  : 1;  /* 欠压故障 */
        uint16 over_current_err   : 1;  /* 过流故障 */
        uint16 over_temp_err      : 1;  /* 过温故障 */
        uint16 sensor_disconnect  : 1;  /* 传感器断开 */
        uint16 comm_timeout       : 1;  /* 通信超时 */
        uint16 eeprom_err         : 1;  /* 存储器故障 */
        uint16 motor_block_err    : 1;  /* 电机堵转故障 */

        uint16 reserved           : 8;  /* 保留位 */

    } bits;

    uint16 value;                     /* 故障字整体值 */

} MOTOR_FAULT_BITFIELD;

  在该示例中,MOTOR_FAULT_BITFIELD 表示"电机故障位域类型"。其中 bits 用于按位访问具体故障标志,value 用于整体读取或写入故障字。这样的设计方式既方便单独判断某一类故障,也方便进行整字处理和通信传输。

  此外,无论是结构体、枚举还是位域,在变量定义时都建议配套添加必要注释;对于需要初始化的变量,也应根据实际业务逻辑完成初始化,以避免出现未定义行为。

三、基础数据类型重命名

  在嵌入式系统中,基础数据类型的位长度并不是由 C/C++ 语言标准完全固定的,而是由编译器 和**目标平台架构(ABI)**共同决定。不同位宽的平台,例如 8 位、16 位、32 位、64 位 系统,通常会遵循一定的数据模型,但在不同编译环境下也可能存在差异。例如:int、long、pointer 等类型在不同平台中的长度可能并不一致。常见的数据模型包括 ILP32 、LP64 等。因此,在嵌入式项目开发过程中,如果直接使用基础类型,代码在移植到新平台时可能会因为数据长度变化而出现异常。常见位宽平台中的基础数据类型长度差异如下表所示:

数据类型 8bit系统 16bit系统 32bit系统(ILP32/LP32) 64bit系统(LP64)
int 16位 16位 32位 32位
long 32位 32位 32位 64位
指针 16位 16位 32位 64位
double 32位或64位 64位 64位 64位

  对于嵌入式项目而言,尤其是在电机控制器、驱动板或状态采集模块这类需要长期维护和跨平台移植的场景中,建议在项目初期就对基础数据类型进行统一重命名,以减少不同平台带来的兼容性问题。例如,在电机控制项目中,可以统一定义如下基础类型:

c 复制代码
typedef signed char         int8;
typedef short int           int16;
typedef signed int          int32;
typedef long long           int64;

typedef unsigned char       uint8;
typedef unsigned short int  uint16;
typedef unsigned int        uint32;
typedef unsigned long long  uint64;

typedef float               float32;
typedef double              float64;

  通过这种方式,开发人员在阅读代码时可以直接从类型名中判断变量长度,也便于后续接口设计、通信协议定义以及跨平台代码移植。当接手一个新的平台时,如果无法确认当前编译环境下各基础类型的实际位宽,可以编写一个简单的测试程序进行验证。下面给出一个适合在嵌入式项目移植前使用的测试示例:

c 复制代码
#include <stdio.h>

int main(void)
{
    printf("=== Basic Type Size Check ===\n");

    printf("char      : %zu byte(s), %zu bit(s)\n", sizeof(char), sizeof(char) * 8);
    printf("short     : %zu byte(s), %zu bit(s)\n", sizeof(short), sizeof(short) * 8);
    printf("int       : %zu byte(s), %zu bit(s)\n", sizeof(int), sizeof(int) * 8);
    printf("long      : %zu byte(s), %zu bit(s)\n", sizeof(long), sizeof(long) * 8);
    printf("long long : %zu byte(s), %zu bit(s)\n", sizeof(long long), sizeof(long long) * 8);

    printf("float     : %zu byte(s), %zu bit(s)\n", sizeof(float), sizeof(float) * 8);
    printf("double    : %zu byte(s), %zu bit(s)\n", sizeof(double), sizeof(double) * 8);
    printf("pointer   : %zu byte(s), %zu bit(s)\n", sizeof(void *), sizeof(void *) * 8);

    return 0;
}

  在电机控制项目中,这类测试程序通常可用于确认控制板平台下的数据类型长度是否满足控制算法、寄存器映射以及通信数据解析的要求。只有明确了当前平台的数据模型,后续定义 motor_speed_rpm、motor_current_ma、motor_fault_code 等变量时,才能保证数据含义与长度一致。

四、命名中常见的成对词汇与缩写

  在嵌入式软件编码规范中,系统地使用一组清晰、一致的成对术语 和标准缩写,是提升代码可读性、可维护性以及团队协作效率的重要手段。这些术语和缩写共同构成了项目内部的"公共语言"。

  在电机控制系统中,这一点尤其重要。例如,驱动初始化、速度控制、状态切换、故障恢复、参数更新等操作,往往会在多个模块中反复出现。如果没有统一术语,代码风格很容易混乱,进而增加维护成本。

4.1 成对术语:定义清晰的行为边界

  在项目设计中,很多操作都天然具有成对关系,例如"创建/销毁""启动/停止""加锁/解锁""获取/释放"等。对这些行为使用统一术语,可以帮助开发人员快速理解模块职责和接口边界。

  下表给出了在嵌入式电机控制项目中常见的成对术语示例:

类别 术语对 核心含义与规范应用
生命周期 create / destroy 描述资源、对象或模块的完整生命周期,必须成对出现,确保资源无泄漏。
生命周期 initialize(init) / deinitialize(deinit) 描述模块初始化与反初始化,例如电机控制器启动前后的资源配置与释放。
生命周期 open / close 常用于设备接口、串口、CAN 通道等资源的打开与关闭。
生命周期 start / stop 常用于电机、任务、定时器或控制流程的启动与停止。
数据操作 add / remove 强调对无序集合或列表的添加与移除。
数据操作 insert / delete 强调对有序集合、缓冲区或队列中指定位置的数据操作。
数据操作 increment(inc) / decrement(dec) 表示计数、索引或调节量的递增与递减。
数据操作 send / receive 表示通信数据的发送与接收,例如 CAN 帧或串口数据。
数据操作 put / get 表示数据写入与读取,例如设置参数或获取状态值。
访问控制 lock / unlock 控制共享资源访问,例如电机状态表、全局配置区。
访问控制 acquire / release 表示资源申请与释放,常见于驱动资源或外设句柄管理。
状态与顺序 begin / end 用于定义范围边界或流程起止。
状态与顺序 first / last 用于表示首元素与尾元素。
状态与顺序 next / previous(prev) 表示顺序关系,常用于链表、队列或状态切换。
状态与顺序 old / new 表示状态更新前后,例如电机旧速度与新速度。
范围与极值 min / max 表示最小值与最大值。
范围与方向 up / down 表示方向变化,例如速度升高/降低、电流上升/下降。

  在电机控制系统中,可以将这些术语直接应用到接口命名中。例如:

c 复制代码
Motor_Init();
Motor_Deinit();

Motor_Start();
Motor_Stop();

MotorSpeed_Get();
MotorSpeed_Set();

MotorFault_Lock();
MotorFault_Unlock();

  这种命名方式的优点在于:只要看到函数名,开发人员就能大致判断函数的行为边界,而不需要先深入查看实现代码。

  在项目中使用成对词汇时,建议遵循以下原则:

  一致性 :一旦某个模块或某类数据结构确定了一组术语,整个项目中应保持一致。例如已经使用 add/remove,就不要在其他地方混用 add/delete。

  对称性 :逻辑上成对的操作应尽量成对实现。例如存在 MotorBuffer_Alloc(),就应有对应的 MotorBuffer_Free()。

  语义清晰 :对于 get/release 这类可能具有多义性的词汇,应在项目规范中明确其具体含义,避免理解歧义。

  对于电机控制项目而言,建议优先围绕"初始化、使能、启动、停止、采集、保护、恢复"等核心行为建立一组统一术语,并在各模块中重复使用,从而形成一致的接口风格。

4.2 标准缩写:在简洁与清晰之间取得平衡

  缩写的作用在于精简命名长度,但前提是不能引入歧义。对于嵌入式项目,合理使用标准缩写可以显著减少变量名、函数名和结构体成员名的长度,使代码更紧凑,但同时仍保持可读性。在电机控制项目中,下面这些缩写都比较常见且推荐使用:

缩写 全称 使用场景
cfg configuration 配置结构体或配置参数,如 motor_cfg、drv_cfg
init initialize 初始化函数,如 Motor_Init()
deinit deinitialize 反初始化函数,与 init 配对使用
cnt count 计数器变量,如 fault_cnt、retry_cnt
prev previous 前一个状态或前一个值,如 prev_speed_rpm
next next 下一个状态或下一节点,如 next_state
ptr pointer 指针变量,如 motor_msg_ptr
val value 一般数值变量,如 current_val、threshold_val
tmp temporary 临时变量,如 tmp_speed
buf buffer 缓冲区或缓存,如 tx_buf、rx_buf
msg message 消息或通信数据,如 ctrl_msg、diag_msg
reg register 寄存器变量,如 ctrl_reg、status_reg
sta / stat status 状态变量或状态寄存器,如 motor_sta、sys_stat
idx index 数组或列表索引,如 motor_idx
len length 长度变量,如 frame_len、data_len
min / max minimum / maximum 极值变量,一般不再额外缩写

  例如,在电机控制系统中,可以写成下面这种形式:

c 复制代码
MOTOR_CFG_STRUCT motor_cfg;
uint16 motor_retry_cnt;
uint16 prev_speed_rpm;
uint16 next_speed_rpm;
uint8 *motor_msg_ptr;
uint8 tx_buf[8];
uint8 rx_buf[8];
uint16 motor_fault_stat;

  这些缩写都是行业内比较通用且容易理解的形式,既节省命名长度,又不会给阅读者带来额外理解负担。

  在使用缩写时,建议遵循以下要求:

  建立统一映射表:团队内部应明确每一个缩写的含义,并将其作为新成员必须遵守的命名文档,以减少个人习惯造成的风格分裂。

  非惯用词优先写全称 :对于不常用、不直观的词汇,应优先使用全称。例如 ack(acknowledge)虽然常见,但如果面向新成员较多的项目,也可以适当说明其含义。

  模块内风格一致 :如果某个模块已经约定使用 cfg,则后续就不要混用 config;如果已经使用 stat,则不要再混用 status。

  在电机控制项目中,建议优先统一配置类、状态类、消息类、寄存器类、索引类变量的缩写方式。这样在查看速度环、电流环、故障诊断、通信控制等模块代码时,开发人员可以迅速识别变量角色,提高维护效率。

小结

统一命名与变量设计,是提升代码可维护性和团队协作效率的关键:

  • 代码读起来更直观

  • 接口行为更可预测

  • Bug 容易定位,开发效率提升

  下一篇,我们将进入数据结构与类型设计,掌握struct、enum、bitfield以及跨平台类型问题,让代码更规范、更安全。

相关推荐
深圳老胡12 小时前
STM32CubeMX 生成 CMake 工程,用 VSCode 编译与调试的完整流程
笔记·stm32·单片机·代码规范
星星泡饭吃19 小时前
【信息安全】越权防护设计评审指南
代码规范
GlobalSign数字证书20 小时前
申请 EV 代码签名证书需要准备哪些材料?
代码规范
深圳老胡20 小时前
STM32F4 OTA 升级双 App 方案设计与实现
笔记·stm32·单片机·代码规范
深圳老胡1 天前
STM32F4 OTA升级:单App与双App方案优缺点对比
笔记·stm32·单片机·代码规范
这个DBA有点耶2 天前
Change Buffer深入:二级索引写入的隐形加速器与它的代价
数据库·mysql·代码规范
知守观3 天前
一个半天需求干了三天:代码腐化的五个信号与自查命令
java·后端·代码规范
深圳老胡3 天前
STM32 MCU 国产替代型号简介
笔记·stm32·单片机·代码规范
深圳老胡3 天前
STM32 在 VSCode 下 -O0 与 -Os 编译条件的区别
笔记·stm32·单片机·代码规范
liangsheng_g5 天前
SpringAOP两套代理创建路线设计哲学与开源实践
spring·开源·代码规范