4、代码风格与规范:Google C++ Style Guide、Python PEP8、静态检查工具(cpplint/pylint)、clang-format配置

说实话,代码风格这事儿,我年轻的时候真没当回事。

那时候觉得,能跑就行,管它缩进几个空格?直到有一次,我接手了一个前任留下的嵌入式项目。好家伙,那代码简直像被猫踩过键盘——有的地方缩进2格,有的地方8格,变量名有拼音有英文还有乱码。我花了整整三天才理清一个中断服务函数。从那以后,我彻底明白了:代码是写给人看的,顺便给机器执行

4.1 为什么嵌入式项目更需要代码规范?

你想想看,嵌入式开发有个特点:硬件耦合度高,调试手段有限。很多时候,你没法像写纯软件那样随便打日志、上断点。代码一旦混乱,出bug了你根本不知道是逻辑问题还是风格问题。

我个人习惯,在项目启动的第一天,就把规范文件扔进仓库根目录。这叫「先立规矩,再干活」。别等到代码写了两万行再回头改格式,那代价太大了。

核心原则:嵌入式代码的读者,50%是未来的你自己,50%是你的同事。规范不是束缚,是保护。

4.2 Google C++ Style Guide 在嵌入式中的取舍

Google C++ Style Guide 是业界公认的标杆。但嵌入式场景有些特殊,我遇到过不少团队生搬硬套,结果反而别扭。

4.2.1 命名规范

Google 风格要求:

  • 文件名:小写加下划线,如 uart_driver.cpp
  • 类型名:首字母大写,如 class UartDriver
  • 变量名:小写加下划线,如 int baud_rate
  • 常量:k 开头,如 const int kMaxBufferSize = 256

嗯,这里要注意一点。嵌入式里经常操作寄存器,我建议寄存器映射的结构体成员可以用全大写,跟芯片手册保持一致。比如:

// 寄存器映射,遵循芯片手册命名
typedef struct {
    volatile uint32_t CR1;    // 控制寄存器1
    volatile uint32_t CR2;    // 控制寄存器2
    volatile uint32_t SR;     // 状态寄存器
} UART_Registers;

这样做的好处是,你对着数据手册调代码时,不用来回翻译名字。我曾经因为把 SR 改成了 status_register,结果查bug时对着手册找了半天才反应过来——浪费了整整一下午。

4.2.2 缩进与括号

Google 风格用2空格缩进,左括号不换行:

void UartInit(int baud_rate) {
    if (baud_rate > 115200) {
        SetHighSpeedMode();
    }
}

我个人更推荐4空格缩进。为什么?嵌入式代码嵌套深,2空格在1280×1024的屏幕上看着像没缩进一样。但如果你团队统一用2空格,我也没意见——关键是统一。

小技巧:在项目根目录放一个 .clang-format 文件,所有成员用同一个格式化配置。谁提交的代码格式不对,CI直接拒绝。省去扯皮的功夫。

4.3 Python PEP8 在嵌入式工具链中的应用

嵌入式项目里,Python 通常用来写脚本:烧录脚本、测试脚本、数据解析脚本。别觉得脚本就可以随便写,我见过一个500行的烧录脚本,因为缩进混乱,跑一次报三次错。

PEP8 的核心要求其实就几条:

  • 缩进用4个空格,不用Tab
  • 每行不超过79个字符
  • 函数和类之间空两行
  • 导入库分行写

举个例子,一个规范的烧录脚本片段:

import os
import sys
import time

from serial import Serial
from serial.tools import list_ports


def find_device(vid, pid):
    """查找指定VID/PID的串口设备"""
    for port in list_ports.comports():
        if port.vid == vid and port.pid == pid:
            return port.device
    return None


def flash_firmware(port, firmware_path):
    """烧录固件到目标设备"""
    if not os.path.exists(firmware_path):
        raise FileNotFoundError(f"固件文件不存在: {firmware_path}")
    # 烧录逻辑...
    pass

你看,函数名小写加下划线,常量全大写,注释用英文。这些细节看似琐碎,但当你需要在一个月后回头改脚本时,你会感谢现在的自己。

避坑指南:我曾经在Python脚本里混用了Tab和空格,结果在Linux服务器上跑的时候直接报 IndentationError。排查了半小时才发现是编辑器自动把Tab转成了空格,而另一台机器没转。从此以后,我所有编辑器都设置成「插入空格代替Tab」。

4.4 静态检查工具:让机器替你盯代码

人眼检查代码风格,效率低还容易漏。静态检查工具就是你的「代码警察」。

4.4.1 cpplint:C++ 代码的守门员

cpplint 是 Google 官方出的 C++ 风格检查工具。用法很简单:

# 安装
pip install cpplint

# 检查单个文件
cpplint uart_driver.cpp

# 检查整个目录
cpplint --recursive src/

它会告诉你:哪行超过80字符了,哪个变量命名不规范了,哪个头文件顺序不对了。我第一次跑 cpplint 时,一个200行的文件报出了40多个警告——脸都绿了。

但说实话,有些警告可以忽略。比如嵌入式里经常用 volatile,cpplint 可能会报 Runtime/references 之类的。我建议在项目根目录放一个 CPPLINT.cfg

set noparent
filter=-runtime/references
filter=-readability/todo
filter=-build/include_order

这样只保留真正重要的检查项,避免噪音。

4.4.2 pylint:Python 脚本的体检医生

对于 Python 脚本,pylint 是标配。用法:

# 安装
pip install pylint

# 检查脚本
pylint flash_tool.py

# 生成配置文件
pylint --generate-rcfile > .pylintrc

pylint 的评分系统挺有意思,满分10分。我一般要求团队脚本不低于8分。低于8分的,打回重改。

实战建议:把静态检查集成到 Git 的 pre-commit hook 里。每次提交代码前自动跑一遍,不过关就不让提交。这样能保证仓库里的每一行代码都是干净的。

4.5 clang-format 配置:一键格式化

手动调整缩进和对齐?太原始了。clang-format 可以自动格式化你的 C/C++ 代码。

一个适合嵌入式项目的 .clang-format 配置示例:

BasedOnStyle: Google
IndentWidth: 4
UseTab: Never
BreakBeforeBraces: Attach
AllowShortFunctionsOnASingleLine: None
ColumnLimit: 100
PointerAlignment: Left
AccessModifierOffset: -4

解释一下关键项:

  • BasedOnStyle: Google —— 以 Google 风格为基础
  • IndentWidth: 4 —— 我改成了4空格,前面说过了
  • ColumnLimit: 100 —— 嵌入式代码注释多,80太紧,100比较舒服
  • PointerAlignment: Left —— int* p 而不是 int *p,更清晰

用法也简单:

# 格式化单个文件
clang-format -i uart_driver.cpp

# 格式化整个目录
find src/ -name "*.cpp" -o -name "*.h" | xargs clang-format -i

我的习惯:在 VS Code 里配置保存时自动格式化。写代码时完全不用管格式,按 Ctrl+S 的瞬间,代码自动变得整整齐齐。这种「无痛规范」的方式,团队接受度最高。

4.6 总结一下

代码风格这事儿,说白了就是「约定大于配置」。你不需要记住每一条规则,但你需要:

  1. 选定一套规范(Google C++ / PEP8)
  2. 用工具自动检查(cpplint / pylint)
  3. 用工具自动格式化(clang-format)
  4. 把检查集成到 CI 流程里

做到这四点,你的代码仓库就会像一本印刷精美的书,而不是一堆草稿纸。相信我,三个月后的你,会感谢现在坚持规范自己的你。