一、代码规范与风格:命名规范、缩进排版、注释规范与代码块组织

各位同学,咱们今天聊点实在的。

代码规范这事儿,说大不大,说小不小。我见过太多项目,功能跑得飞起,但代码一打开,简直像进了迷宫。你想想看,一个变量叫 a,另一个叫 tmp,第三个叫 data2…… 三个月后你自己都看不懂写了啥。

所以,规范不是束缚,是解放。它让你和未来的自己,以及你的队友,都能少掉几根头发。

1.1 命名规范:名字起得好,bug 找得少

命名是代码的第一张脸。我个人习惯,看到名字就能猜出八九分用途,这才算及格。

1.1.1 变量命名

  • 小驼峰或下划线风格:嵌入式 C 里,我推荐用下划线风格。比如 adc_value、timer_count。清晰,一眼能看出含义。
  • 不要用单字母:除了循环里的 i、j,其他情况尽量别用。我在项目中遇到过有人用 p 表示指针,结果全局搜索出来 200 多个 p…… 那场面,简直灾难。
  • 布尔变量用 is/has/can 开头:比如 is_ready、has_error。读起来像自然语言,舒服。

避坑指南:我曾经接手过一个老项目,变量名全是拼音缩写,比如 csq 代表“传输器启动”。你猜我花了多久才搞明白?整整两天。所以,宁可名字长一点,也别用缩写。

1.1.2 函数命名

  • 动词 + 名词:比如 read_sensor()、set_pwm_duty()。一看就知道这个函数要干嘛。
  • 返回值类型暗示:如果函数返回布尔值,用 is_、check_ 开头。比如 is_buffer_full()。
  • 模块前缀:在大型项目中,我习惯加模块前缀。比如 uart_send_byte()、timer_start()。这样即使函数名重复,也不会冲突。

1.1.3 宏命名

  • 全大写 + 下划线:这是铁律。比如 #define BUFFER_SIZE 256、#define GPIO_PIN_13 13。
  • 避免魔法数字:别在代码里直接写 if (timeout > 1000)。用宏定义 #define TIMEOUT_MS 1000。为什么?因为半年后你看到 1000,根本不知道它代表什么。

小技巧:宏定义里如果涉及表达式,记得加括号。比如 #define SQUARE(x) ((x)*(x))。否则 SQUARE(1+2) 会变成 1+2*1+2,结果等于 5 而不是 9。我当年就踩过这个坑。

1.1.4 文件命名

  • 模块名 + 功能:比如 uart_driver.c、timer_config.h。不要用 test1.c、final_v2.c 这种。
  • 头文件与源文件同名:uart_driver.c 对应 uart_driver.h。这是基本素养。

1.2 缩进与排版:让代码呼吸起来

缩进不是风格问题,是逻辑问题。我见过有人用 Tab,有人用空格,还有人混用。结果在不同编辑器里,代码像被狗啃过一样。

1.2.1 缩进风格

  • 推荐 4 空格:我个人习惯用 4 个空格。Tab 在不同环境下显示宽度不同,容易乱。
  • 大括号位置:我推荐 Allman 风格,大括号单独一行。比如:
if (condition)
{
    do_something();
}
else
{
    do_other();
}

为什么?因为大括号对齐,一眼就能看出代码块的起止。尤其是嵌套多层时,这种风格救过我好几次。

1.2.2 空行与空格

  • 函数之间空一行:不同函数之间用空行隔开,视觉上更清晰。
  • 操作符前后加空格:比如 a = b + c; 而不是 a=b+c;。读起来不费劲。
  • 逗号后面加空格:func(a, b, c); 而不是 func(a,b,c);。

注意:不要为了省几行代码,把逻辑挤在一起。代码是给人读的,不是给机器读的。机器不嫌累,人嫌。

1.3 注释规范:写注释是给自己留后路

说实话,我最怕看到两种注释:一种是完全没有,另一种是废话连篇。比如 i++; // i 加 1。这种注释写了等于没写。

1.3.1 文件头注释

每个源文件和头文件,都应该有一个文件头。我习惯的格式是:

/*
 * File: uart_driver.c
 * Author: 你的名字
 * Date: 2024-01-15
 * Description: UART 驱动模块,提供初始化、发送、接收功能
 * History:
 *   2024-01-15: 初始版本
 *   2024-03-20: 增加 DMA 支持
 */

这个注释有什么用?当你一年后回头看代码,或者新人接手时,能快速知道这个文件是干嘛的。

1.3.2 函数头注释

每个函数都应该有注释,说明它的功能、参数、返回值。比如:

/**
 * @brief 初始化 UART 外设
 * @param baudrate: 波特率,如 115200
 * @param data_bits: 数据位,可选 7/8/9
 * @return 0 成功,-1 失败
 */
int uart_init(uint32_t baudrate, uint8_t data_bits);

我在项目中遇到过,有人写函数不写注释,结果调用者传参顺序搞反了,调试了整整一个下午。嗯,从那以后,我要求团队每个函数都必须有头注释。

1.3.3 关键逻辑注释

  • 复杂算法:比如 FFT、PID 控制,一定要写清楚思路。
  • 边界条件:比如 if (count == MAX_COUNT - 1) 这种,注释说明为什么是 -1。
  • 临时方案:如果代码里有 workaround,一定要标注 TODO 或 FIXME。否则后来者会以为这是正确写法。

避坑指南:我曾经在一个驱动里看到一段诡异的延时代码,没有任何注释。后来才知道,那是为了等硬件稳定。但因为没有注释,被下一个工程师优化掉了,结果整个系统崩溃。所以,任何非直观的代码,都必须加注释。

1.4 代码块组织原则:把代码当文章写

代码块组织,说白了就是怎么把代码排布得清晰易读。我把它总结为三个原则:

1.4.1 单一职责原则

一个函数只做一件事。比如 read_sensor() 只负责读传感器,不要在里面同时做滤波和显示。如果函数超过 50 行,就该考虑拆分了。

1.4.2 相关代码放一起

变量声明、初始化、使用,尽量靠近。比如:

int fd = open_device();
if (fd < 0)
{
    return -1;
}

// 使用 fd 进行读写操作
read_data(fd, buffer, size);

而不是把 fd 声明在文件开头,然后隔了 200 行才用。那样你翻回去找声明,累不累?

1.4.3 减少嵌套深度

嵌套超过 3 层,代码就很难读了。我常用的方法是提前返回:

// 不好的写法
if (ptr != NULL)
{
    if (ptr->data != NULL)
    {
        process(ptr->data);
    }
}

// 好的写法
if (ptr == NULL) return;
if (ptr->data == NULL) return;
process(ptr->data);

你看,这样是不是清爽多了?

1.5 知识体系总览

下面这张图,是我自己总结的代码规范知识体系。你可以把它当作一个检查清单。

代码规范与风格 命名规范 变量:下划线风格 函数:动词+名词 宏:全大写+下划线 文件:模块名+功能 缩进与排版 4空格缩进 大括号单独一行 操作符前后空格 函数间空行分隔 注释规范 文件头注释 函数头注释 代码块组织 单一职责原则 减少嵌套深度

这张图把四个核心模块串起来了。你写代码时,可以对照着检查:命名规范吗?缩进整齐吗?注释到位吗?代码块组织合理吗?

最后说一句:规范不是一天养成的。刚开始可能会觉得麻烦,但坚持一个月,你会发现写出来的代码自己都爱看。嗯,这就是进步。

可可精品资料库