第二章 命名规则:变量、函数、宏、文件、结构体的命名规范
好,咱们直接进入正题。命名这件事,说大不大,说小不小。我见过太多项目,代码逻辑写得挺漂亮,一看变量名就头大——a、b、temp、data1、data2……你想想看,三个月后你自己回来看,还能记得 temp 是干什么的吗?
我个人习惯是:命名就是给代码写注释。一个好的名字,能让读代码的人一眼就明白这个变量是干嘛的、什么类型、作用域多大。今天咱们就把嵌入式开发里最常见的几种命名规范掰开揉碎讲清楚。
2.1 匈牙利命名法:类型即前缀
匈牙利命名法,说白了就是在变量名前加一个或几个小写字母,表示它的类型。比如 iCount 表示整型计数器,szName 表示以零结尾的字符串。
我在项目中遇到过一件事:一个老工程师写的代码,变量名全是 pBuf、dwLen、hFile 这种。刚开始我觉得啰嗦,后来发现调试时太方便了——看到 p 就知道是指针,看到 dw 就知道是32位无符号整数,根本不用回头查定义。
嵌入式常用匈牙利前缀速查表
| 前缀 | 类型 | 示例 |
|---|---|---|
| i / n | int | iCounter, nIndex |
| ui / u | unsigned int | uiFlags, uLen |
| p | 指针 | pBuffer, pNode |
| sz | 以'\0'结尾的字符串 | szName, szPath |
| dw | DWORD (32位无符号) | dwTickCount |
| b | BOOL / bool | bIsReady, bFlag |
| h | 句柄 | hUart, hTimer |
| f | float | fTemperature |
注意:匈牙利命名法在纯C的嵌入式项目里很实用,但在C++或现代C项目中容易显得冗余。我个人建议:团队统一风格就好,别混用。我曾经在一个项目里看到有人用 iCount,有人用 count,还有人用 cnt——那叫一个乱。
2.2 驼峰命名法:大小写来分段
驼峰命名法分两种:小驼峰和大驼峰。小驼峰首字母小写,后面每个单词首字母大写,比如 uartSendData。大驼峰首字母也大写,比如 UartSendData。
我个人习惯:变量用小驼峰,函数用大驼峰。为什么?因为一眼就能区分「这是个变量」还是「这是个函数」。你想想看,读代码时看到 uartSendData 和 UartSendData,大脑的反应速度是不一样的。
// 小驼峰:变量
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_data、crc_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.c、test2.c——这种项目,三个月后连作者自己都找不到东西。
我个人习惯:文件名 = 模块名 + 功能描述。比如 uart_driver.c、uart_driver.h、crc_calculate.c。这样你一看文件名就知道这个文件是干什么的。
| 推荐命名 | 不推荐命名 | 说明 |
|---|---|---|
| uart_driver.c | uart.c | 太笼统,不知道是驱动还是应用 |
| crc16_calculate.c | crc.c | 不知道是CRC8还是CRC16 |
| flash_storage.c | storage.c | 不知道是Flash还是SD卡 |
| main_control.c | main.c | 所有main.c混在一起会冲突 |
避坑指南:我曾经在一个项目里看到有人把 uart.c 和 uart.h 放在不同目录下,结果编译时链接错了文件。从那以后,我要求团队:.c和.h文件必须同名,且放在同一目录。别问为什么,血的教训。
2.5 结构体命名:类型与实例要分清
结构体命名有个经典问题:类型名和实例名容易混淆。比如你定义了一个结构体类型 uart,然后又声明了一个变量也叫 uart——编译器不会报错,但读代码的人会疯掉。
我建议的规则:
- 结构体类型名:用大驼峰或全大写+下划线,加后缀
_t表示类型。比如UartConfig_t、CRC_PARAMS_T。 - 结构体变量名:用小驼峰或小写+下划线。比如
uartConfig、crc_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_SIZE 和 maxSize 混用,结果调试时花了半小时才发现 maxSize 是个宏——因为它被 #define 成了 1024,但看起来像个变量。
2.7 总结:我的命名三原则
讲了这么多,其实核心就三条:
- 一致性:整个项目用一种风格,别混搭。团队里统一规范,写在文档里。
- 自描述性:看到名字就知道用途、类型、作用域。别用
a、b、tmp这种。 - 可搜索性:名字要方便 grep。比如
uart_send_data比send好搜一百倍。
最后说一句:命名规范不是教条,是工具。它的目的是让代码更容易读、更容易维护。你想想看,如果每个变量名都要猜半天,那还写什么代码?
下一章咱们聊聊注释规范——嗯,这个坑更大,我到时候给你们讲讲我见过的「注释灾难」。