第二章 命名规则:变量、函数、宏、文件、结构体的命名规范

好,咱们直接进入正题。命名这件事,说大不大,说小不小。我见过太多项目,代码逻辑写得挺漂亮,一看变量名就头大——abtempdata1data2……你想想看,三个月后你自己回来看,还能记得 temp 是干什么的吗?

我个人习惯是:命名就是给代码写注释。一个好的名字,能让读代码的人一眼就明白这个变量是干嘛的、什么类型、作用域多大。今天咱们就把嵌入式开发里最常见的几种命名规范掰开揉碎讲清楚。

2.1 匈牙利命名法:类型即前缀

匈牙利命名法,说白了就是在变量名前加一个或几个小写字母,表示它的类型。比如 iCount 表示整型计数器,szName 表示以零结尾的字符串。

我在项目中遇到过一件事:一个老工程师写的代码,变量名全是 pBufdwLenhFile 这种。刚开始我觉得啰嗦,后来发现调试时太方便了——看到 p 就知道是指针,看到 dw 就知道是32位无符号整数,根本不用回头查定义。

嵌入式常用匈牙利前缀速查表

前缀类型示例
i / nintiCounter, nIndex
ui / uunsigned intuiFlags, uLen
p指针pBuffer, pNode
sz以'\0'结尾的字符串szName, szPath
dwDWORD (32位无符号)dwTickCount
bBOOL / boolbIsReady, bFlag
h句柄hUart, hTimer
ffloatfTemperature

注意:匈牙利命名法在纯C的嵌入式项目里很实用,但在C++或现代C项目中容易显得冗余。我个人建议:团队统一风格就好,别混用。我曾经在一个项目里看到有人用 iCount,有人用 count,还有人用 cnt——那叫一个乱。

2.2 驼峰命名法:大小写来分段

驼峰命名法分两种:小驼峰和大驼峰。小驼峰首字母小写,后面每个单词首字母大写,比如 uartSendData。大驼峰首字母也大写,比如 UartSendData

我个人习惯:变量用小驼峰,函数用大驼峰。为什么?因为一眼就能区分「这是个变量」还是「这是个函数」。你想想看,读代码时看到 uartSendDataUartSendData,大脑的反应速度是不一样的。

// 小驼峰:变量
uint8_t uartBuffer[256];
uint16_t dataLength;
bool isTransmitting;

// 大驼峰:函数
void UartSendData(uint8_t *pData, uint16_t len);
uint16_t CrcCalculate(uint8_t *pBuf, uint16_t size);

小技巧:如果变量名太长,可以适当缩写,但一定要保持一致性。比如 transmitBuffer 可以缩写成 txBuffer,但别今天用 txBuf,明天用 transBuf。我一般会在项目文档里列一个「缩写对照表」。

2.3 下划线命名法:清晰且易读

下划线命名法,也叫蛇形命名法。所有字母小写,单词之间用下划线分隔。比如 uart_send_datacrc_calculate

这种风格在Linux内核和很多嵌入式RTOS里非常常见。说实话,我个人觉得它比驼峰更易读——尤其是当变量名包含多个单词时,下划线就像天然的分隔符。

// 下划线命名法
#define MAX_BUFFER_SIZE  1024
static uint8_t rx_buffer[256];
void uart_send_data(uint8_t *data, uint16_t len);

我的建议:在嵌入式C项目里,宏和枚举用全大写+下划线变量和函数用小写+下划线。这样你看到 MAX_BUF_SIZE 就知道是宏,看到 uart_init 就知道是函数。嗯,这个规则我用了十几年,从来没出过问题。

2.4 文件命名:模块化思维

文件命名其实比变量命名更容易被忽略。我见过有人把所有代码塞进一个 main.c 里,也见过有人把文件名起成 test1.ctest2.c——这种项目,三个月后连作者自己都找不到东西。

我个人习惯:文件名 = 模块名 + 功能描述。比如 uart_driver.cuart_driver.hcrc_calculate.c。这样你一看文件名就知道这个文件是干什么的。

推荐命名不推荐命名说明
uart_driver.cuart.c太笼统,不知道是驱动还是应用
crc16_calculate.ccrc.c不知道是CRC8还是CRC16
flash_storage.cstorage.c不知道是Flash还是SD卡
main_control.cmain.c所有main.c混在一起会冲突

避坑指南:我曾经在一个项目里看到有人把 uart.cuart.h 放在不同目录下,结果编译时链接错了文件。从那以后,我要求团队:.c和.h文件必须同名,且放在同一目录。别问为什么,血的教训。

2.5 结构体命名:类型与实例要分清

结构体命名有个经典问题:类型名和实例名容易混淆。比如你定义了一个结构体类型 uart,然后又声明了一个变量也叫 uart——编译器不会报错,但读代码的人会疯掉。

我建议的规则:

  • 结构体类型名:用大驼峰或全大写+下划线,加后缀 _t 表示类型。比如 UartConfig_tCRC_PARAMS_T
  • 结构体变量名:用小驼峰或小写+下划线。比如 uartConfigcrc_params
// 结构体类型定义
typedef struct {
    uint32_t baudRate;
    uint8_t  dataBits;
    uint8_t  stopBits;
    uint8_t  parity;
} UartConfig_t;

// 结构体变量声明
static UartConfig_t uartConfig;
UartConfig_t *pUartConfig = &uartConfig;

一个小习惯:我写结构体时,一定会把成员变量按「大小对齐」排序——先放32位的,再放16位的,最后放8位的。这样既能减少内存碎片,也让结构体看起来更整齐。嗯,这个习惯是从一次内存优化项目里养成的。

2.6 宏命名:全大写是铁律

宏的命名规则其实最简单:全大写 + 下划线。没有例外,没有商量余地。为什么?因为宏是预处理阶段替换的,它不像函数那样有类型检查,如果不用全大写,很容易被误当成函数调用。

// 正确的宏命名
#define GPIO_PIN_0      (1U << 0)
#define TIMEOUT_MS      1000
#define IS_BIT_SET(val, bit)   ((val) & (1U << (bit)))

// 错误的宏命名
#define timeout_ms      1000   // 容易和变量混淆
#define isBitSet(val, bit) ... // 容易和函数混淆

核心原则:宏的名字要让人一眼就看出「这是个宏」。全大写+下划线就是最直接的信号。我见过有人用 MAX_SIZEmaxSize 混用,结果调试时花了半小时才发现 maxSize 是个宏——因为它被 #define 成了 1024,但看起来像个变量。

2.7 总结:我的命名三原则

讲了这么多,其实核心就三条:

  1. 一致性:整个项目用一种风格,别混搭。团队里统一规范,写在文档里。
  2. 自描述性:看到名字就知道用途、类型、作用域。别用 abtmp 这种。
  3. 可搜索性:名字要方便 grep。比如 uart_send_datasend 好搜一百倍。

最后说一句:命名规范不是教条,是工具。它的目的是让代码更容易读、更容易维护。你想想看,如果每个变量名都要猜半天,那还写什么代码?

下一章咱们聊聊注释规范——嗯,这个坑更大,我到时候给你们讲讲我见过的「注释灾难」。