目录

引言

1.1 起源与目的

1.2 规则体系结构

一.变量与类型(Rule 1–9)

Rule 1.1 — 变量必须显式初始化(Mandatory)

Rule 1.2 — 使用固定宽度整数类型(Mandatory)

Rule 1.3 — 避免未定义行为的类型转换(Required)

Rule 1.4 — 常量和宏应使用 const 或枚举(Required)

Rule 1.5 — 结构体字段必须显式初始化(Required)

Rule 1.6 — 非结构体跨文件变量通过接口访问(Required)

Rule 1.7 — 复杂结构体跨文件仅在必要时直接共享(Required)

Rule 1.8 — 避免变量隐藏(shadowing)(Required)

Rule 1.9 — 变量命名应清晰反映用途和作用域(Advisory)

二.条件控制(Rule 10–28)

Rule 2.1 — 禁止在条件表达式中使用赋值(Mandatory)

Rule 2.2 — 条件表达式必须明确(Mandatory)

Rule 2.3 — 布尔表达式应显式使用比较(Required)

Rule 2.4 — 三元运算符条件部分必须明确(Required)

Rule 2.5 — 条件中不允许混合不同类型(Required)

Rule 2.6 — if/else 必须使用块 {} 包围(Required)

Rule 2.7 — switch 必须有默认分支(Required)

Rule 2.8 — switch case 标签值必须唯一(Required)

Rule 2.9 — switch 不允许穿透(fallthrough)(Required)

Rule 2.10 — 循环条件必须可确定(Required)

Rule 2.11 — 循环计数器类型必须明确(Required)

Rule 2.12 — goto 禁止跳入或跳出条件块(Required)

Rule 2.13 — 逻辑表达式不允许副作用(Required)

Rule 2.14 — 条件中禁止使用魔法数字(Required)

Rule 2.15 — 对指针判断必须显式与 NULL 比较(Required)

Rule 2.16 — 条件中不允许使用可变位操作(Required)

Rule 2.17 — 条件避免依赖实现定义行为(Required)

Rule 2.18 — 复杂条件应拆分为子表达式(Advisory)

三.循环控制(Rule 29–50)

Rule 3.1 — 循环必须保证终止条件可达(Mandatory)

Rule 3.2 — 循环计数器类型必须固定宽度(Mandatory)

Rule 3.3 — 循环体内禁止修改循环变量外部依赖(Required)

Rule 3.4 — 禁止在循环条件中使用赋值(Required)

Rule 3.5 — 循环中避免使用魔法数字(Required)

Rule 3.6 — 避免无限循环,必要时加 Watchdog 或超时检测(Required)

Rule 3.7 — 循环体必须使用花括号(Required)

Rule 3.8 — 循环中禁止复杂表达式(Advisory)

Rule 3.9 — 避免嵌套循环过深(Advisory)

Rule 3.10 — 循环变量只在循环体内使用(Advisory)

Rule 3.11 — do-while 循环条件必须明确(Required)

Rule 3.12 — 循环体内部禁止使用 goto 跳出循环(Required)

Rule 3.13 — 避免循环中动态内存分配(Required)

Rule 3.14 — 循环中禁止修改外部全局状态(Advisory)

Rule 3.15 — 循环计数器避免超过类型范围(Required)

Rule 3.16 — 循环退出条件必须可预测(Required)

Rule 3.17 — 循环体函数调用注意执行时间(Advisory)

Rule 3.18 — 循环中禁止使用浮点变量(Required)

Rule 3.19 — 循环计数器初始化必须明确(Mandatory)

Rule 3.20 — 循环条件避免依赖外部中断触发(Required)

Rule 3.21 — 避免循环体内函数多次修改同一全局变量(Advisory)

Rule 3.22 — 循环计数器尽量局部化(Advisory)

四.函数(Rule 51–80)

Rule 4.1 — 每个函数应有明确的功能(Mandatory)

Rule 4.2 — 函数参数数量应控制(Advisory)

Rule 4.3 — 参数类型应明确且固定宽度(Mandatory)

Rule 4.4 — 使用 const 修饰只读参数(Required)

Rule 4.5 — 避免返回指针指向局部变量(Mandatory)

Rule 4.6 — 避免返回指针指向动态分配的局部对象(Mandatory)

Rule 4.7 — 函数应有明确返回值(Mandatory)

Rule 4.8 — 避免函数内静态变量造成非线程安全(Required)

Rule 4.9 — 函数长度应可控(Advisory)

Rule 4.10 — 避免函数副作用过多(Advisory)

Rule 4.11 — 函数名应清晰反映功能(Advisory)

Rule 4.12 — 避免递归(Required)

Rule 4.13 — 函数参数不要依赖全局状态(Required)

Rule 4.14 — 函数中禁止魔法常量(Required)

Rule 4.15 — 函数内变量初始化(Mandatory)

Rule 4.16 — 函数接口应保持一致性(Advisory)

Rule 4.17 — 避免函数依赖未定义行为(Required)

Rule 4.18 — 函数文档化(Advisory)

Rule 4.19 — 避免函数过长生命周期副作用(Advisory)

Rule 4.20 — 函数内禁止中断敏感操作(Required)

Rule 4.21 — 避免函数中使用全局指针(Required)

Rule 4.22 — 函数内禁止动态内存分配(Required)

Rule 4.23 — 函数返回值应检查(Mandatory)

Rule 4.24 — 避免函数内部副作用修改全局状态(Advisory)

Rule 4.25 — 函数尽量可重入(Required)

Rule 4.26 — 函数异常路径处理(Mandatory)

Rule 4.27 — 避免函数内长时间阻塞(Required)

Rule 4.28 — 函数内部循环控制遵循循环章节规则(Required)

Rule 4.29 — 函数内条件判断简明(Advisory)

Rule 4.30 — 函数参数和返回值类型一致(Required)

五.条件控制(Rule 81–120)

Rule 5.1 — 条件表达式必须明确(Mandatory)

Rule 5.2 — 避免赋值运算出现在条件中(Required)

Rule 5.3 — 条件判断结果应为布尔类型(Advisory)

Rule 5.4 — 避免复杂嵌套条件(Required)

Rule 5.5 — 条件判断边界应清晰(Mandatory)

Rule 5.6 — 避免重复条件(Required)

Rule 5.7 — 避免条件依赖未定义行为(Mandatory)

Rule 5.8 — 使用括号明确逻辑运算优先级(Required)

Rule 5.9 — 避免条件中函数副作用(Required)

Rule 5.10 — 条件表达式应可测试(Advisory)

Rule 5.11 — 避免空条件语句(Required)

Rule 5.12 — 条件判断逻辑应简明(Advisory)

Rule 5.13 — switch 必须包含 default(Mandatory)

Rule 5.14 — switch case 不得重复(Required)

Rule 5.15 — switch case 应覆盖所有枚举(Advisory)

Rule 5.16 — switch 内禁止 fall-through(Required)

Rule 5.17 — 条件表达式应避免副作用(Required)

Rule 5.18 — 使用逻辑常量时显式比较(Required)

Rule 5.19 — 避免条件中多层函数调用(Advisory)

Rule 5.20 — 避免条件短路副作用(Required)

Rule 5.21 — 条件表达式避免魔法数字(Required)

Rule 5.22 — 避免条件判断依赖未初始化变量(Mandatory)

Rule 5.23 — 条件判断分支必须可达(Required)

Rule 5.24 — 条件判断中避免强制类型转换(Required)

Rule 5.25 — 条件逻辑应可单元测试(Advisory)

Rule 5.26 — 条件中避免函数返回未定义值(Mandatory)

Rule 5.27 — 条件表达式应可读性良好(Advisory)

Rule 5.28 — 避免条件嵌套过深(Required)

Rule 5.29 — 条件判断中避免全局状态依赖(Required)

Rule 5.30 — 条件表达式结果类型一致(Required)

Rule 5.31 — 避免条件表达式重复计算(Required)

Rule 5.32 — 避免条件中使用非标准运算符(Required)

Rule 5.33 — 条件逻辑应保持简洁(Advisory)

Rule 5.34 — 条件判断注释应清晰(Advisory)

Rule 5.35 — 条件表达式避免未定义宏(Required)

Rule 5.36 — 避免条件中使用静态变量(Required)

Rule 5.37 — 条件中避免修改数组索引(Required)

Rule 5.38 — 条件判断中避免调用阻塞函数(Required)

Rule 5.39 — 条件表达式结果应与期望类型匹配(Required)

Rule 5.40 — 条件判断应覆盖边界场景(Mandatory)

六.循环控制(Rule 121–143)

Rule 6.1 — 循环必须可控(Mandatory)

Rule 6.2 — 避免无限循环(Required)

Rule 6.3 — 循环边界应明确(Mandatory)

Rule 6.4 — 循环中避免修改循环计数器(Required)

Rule 6.5 — 循环条件表达式应简洁(Advisory)

Rule 6.6 — 避免循环中出现副作用(Required)

Rule 6.7 — 循环变量类型匹配(Mandatory)

Rule 6.8 — 避免循环体空语句(Required)

Rule 6.9 — 循环中避免复杂条件(Advisory)

Rule 6.10 — 避免循环变量溢出(Mandatory)

Rule 6.11 — 循环退出条件应可靠(Required)

Rule 6.12 — 避免循环体中函数副作用(Required)

Rule 6.13 — 循环中避免访问越界数组(Mandatory)

Rule 6.14 — 循环体应易读(Advisory)

Rule 6.15 — 避免循环嵌套过深(Required)

Rule 6.16 — 循环中变量应尽量局部(Advisory)

Rule 6.17 — 循环中避免使用浮点(Required)

Rule 6.18 — 循环条件应避免魔法数字(Required)

Rule 6.19 — 循环体应避免未使用变量(Advisory)

Rule 6.20 — 循环变量不可修改其他全局状态(Required)

Rule 6.21 — 循环中避免调用阻塞函数(Required)

Rule 6.22 — 循环中避免调用可能出错的函数(Required)

Rule 6.23 — 循环变量结果应可单元测试(Advisory)

七.MISRA C:2012 指令(Directives)

Directive 1 — 可移植性检查

Directive 2 — 头文件包含策略

Directive 3 — 注释使用规范

Directive 4 — 代码可读性和结构

Directive 5 — 预处理器指令使用

Directive 6 — 编译器依赖性检查

Directive 7 — 可测试性

Directive 8 — 错误处理策略

Directive 9 — 函数接口设计

Directive 10 — 变量和类型使用

Directive 11 — 循环和控制结构

Directive 12 — 宏定义和常量

Directive 13 — 静态分析工具兼容性

Directive 14 — 库函数使用策略

Directive 15 — 对外接口文档规范

Directive 16 — 安全与关键功能的审查

八.补充内容

8.1 C语言空格使用指南

8.2 复杂条件多行分解规范

8.2.1 方法一:运算符置于行首

8.2.2 方法二:运算符置于行尾

8.2.3 方法三:使用临时布尔变量


引言

1.1 起源与目的

  • MISRA(Motor Industry Software Reliability Association)于 1998 年发布针对汽车嵌入式 C 语言的软件开发规范。
  • 主要目标
    • 提高软件安全性与可靠性
    • 减少 C 语言易出错特性(如隐式类型转换、未初始化变量、指针误用)对汽车 MCU 软件的影响
    • 增强可维护性和可移植性

1.2 规则体系结构

  • 总量:MISRA C:2012 共 143 条规则 + 16 条指令(Directives)

  • 规则编号格式:Rule 章节.序号例如(Rule 1.5)

    • 后半部分“5”表示该章节下具体规则序号
    • 前半部分"1"表示章节或主题分类(如变量与类型)
  • 规则级别

    • Mandatory(强制):必须遵守
    • Required(必需):必须遵守,但在特殊情况下可经例外批准
    • Advisory(建议):推荐遵守,不违反不会直接报错

一.变量与类型(Rule 1–9)

本章节涵盖 9 条规则,核心目标是保证变量的初始化、安全访问和类型一致性,以降低 MCU 软件运行中因未初始化、类型不匹配或错误访问导致的风险。

Rule 1.1 — 变量必须显式初始化(Mandatory)

  • 核心要求:所有变量在使用前必须显式赋初值,避免随机值。

  • 示例

  • static uint16_t counter = 0; // 文件静态变量显式初始化
    
    void InitData(void)
    {
        uint8_t localFlag = 0; // 局部变量显式初始化
    }


Rule 1.2 — 使用固定宽度整数类型(Mandatory)

  • 核心要求:使用明确宽度类型(如 uint8_t, uint16_t, uint32_t)。

  • 示例

    #include <stdint.h>
    uint8_t  sensorStatus;   // 1 字节
    uint16_t sensorValue;    // 2 字节
    uint32_t timestamp;      // 4 字节

Rule 1.3 — 避免未定义行为的类型转换(Required)

  • 核心要求:禁止隐式或危险类型转换,尤其是 signed/unsigned 或不同宽度整数之间的隐式转换。

  • 示例

    uint8_t a = 200;
    int8_t  b = -50;
    // 错误示例:隐式转换可能溢出
    int16_t c = a + b;
    // 安全示例:显式转换
    int16_t c_safe = (int16_t)a + (int16_t)b;


Rule 1.4 — 常量和宏应使用 const 或枚举(Required)

  • 核心要求:避免魔法数字或硬编码,通过const或枚举定义常量。

  • 示例

    #define MAX_SPEED 100   // 不推荐
    
    const uint8_t maxSpeed = 100; // 推荐
    
    typedef enum {
        MODE_OFF = 0,
        MODE_ON  = 1
    } PowerMode_t;

Rule 1.5 — 结构体字段必须显式初始化(Required)

  • 核心要求:结构体声明时或使用前必须显式初始化每个字段。

  • 示例

    typedef struct {
        uint8_t  status;
        uint16_t value;
    } SensorData_t;
    
    SensorData_t sensor = { .status = 0, .value = 0 }; // 显式初始化

Rule 1.6 — 非结构体跨文件变量通过接口访问(Required)

  • 核心要求:跨文件访问标量变量应通过getter/setter接口,而不是直接extern。

  • 示例

    // temp.c
    static uint16_t temperature;
    void Temp_Set(uint16_t v) { temperature = v; }
    uint16_t Temp_Get(void) { return temperature; }
    // main.c
    #include "temp.h"
    
    Temp_Set(100);
    
    uint16_t t = Temp_Get();

Rule 1.7 — 复杂结构体跨文件仅在必要时直接共享(Required)

  • 核心要求:复杂结构体只有在确实需要跨文件共享时才使用extern,否则通过接口封装。

  • 示例

    // shared_data.h
    typedef struct {
        uint8_t  status;
        uint16_t value;
    } SharedData_t;
    extern SharedData_t g_sharedData;
    // shared_data.c
    SharedData_t g_sharedData = {0};


Rule 1.8 — 避免变量隐藏(shadowing)(Required)

  • 核心要求:禁止局部变量覆盖同名全局或静态变量。

  • 示例

    static uint16_t counter = 0;
    
    void Func(void)
    {
        uint16_t counter = 5; // 错误示例,隐藏全局 counter
    }


Rule 1.9 — 变量命名应清晰反映用途和作用域(Advisory)

  • 核心要求:命名应描述变量用途和作用域,便于理解。

  • 嵌入式注意点
    • 全局变量可加 g_ 前缀
    • 文件静态变量可加 s_ 前缀
    • 局部变量无前缀,使用语义化名称
  • 示例

    static uint16_t s_sensorCounter; // 文件静态
    uint8_t g_systemStatus;          // 全局变量
    void ProcessData(uint8_t sensorValue); // 局部参数命名清晰

二.条件控制(Rule 10–28)

本章节涵盖 19 条规则,核心目标是保证条件表达式的安全、逻辑清晰和可维护性,避免由于隐式类型转换、赋值、穿透逻辑或魔法数字导致的 MCU 软件运行异常或潜在风险。


Rule 2.1 — 禁止在条件表达式中使用赋值(Mandatory)

  • 核心要求:条件判断中禁止使用赋值操作,如 if(a = b)。

  • 示例

    if (a == b) { /* 正确 */ }
    if (a = b) { /* 错误 */ }

Rule 2.2 — 条件表达式必须明确(Mandatory)

  • 核心要求:条件必须返回确定布尔值,避免依赖实现定义行为。

  • 示例

    if ((status & FLAG_READY) != 0)
    {
        /* 推荐 */
    }

Rule 2.3 — 布尔表达式应显式使用比较(Required)

  • 核心要求:避免使用整数直接作为条件,必须明确比较。

  • 示例

    if (sensorReady != 0) { /* 正确 */ }
    if (sensorReady) { /* 允许,但推荐第一种 */ }

Rule 2.4 — 三元运算符条件部分必须明确(Required)

  • 核心要求:使用 ?: 时,条件表达式必须明确布尔结果。

  • 示例

  • int val = (a > b) ? 1 : 0; // 正确
    int val = a ? 1 : 0;        // 建议显式比较

Rule 2.5 — 条件中不允许混合不同类型(Required)

  • 核心要求:避免整型、浮点型、枚举混用

  • 示例

    if ((int16_t)a > (int16_t)b) { /* 正确 */ }

Rule 2.6 — if/else 必须使用块 {} 包围(Required)

  • 核心要求:所有 if/else 语句必须用花括号包围

  • 示例

    if (flag) 
    {
        doSomething();
    }
    else
    {
        doOther();
    }

Rule 2.7 — switch 必须有默认分支(Required)

  • 核心要求:每个 switch 必须提供 default 处理

  • 示例

    switch(mode){
        case MODE_OFF: 
            handleOff(); 
            break;
        
        case MODE_ON: 
            handleOn(); 
            break;
        
        default: 
            handleDefault(); 
            break;
    }

Rule 2.8 — switch case 标签值必须唯一(Required)

  • 核心要求:每个 case 的值不可重复

  • 示例

    switch(mode){
        case 0: 
            break;
        
        case 1:      // case 1: 错误,重复
            break;
    
        default:
            break;
    }

Rule 2.9 — switch 不允许穿透(fallthrough)(Required)

  • 核心要求:每个 case 必须显式 break 或 return,禁止隐式穿透

  • 示例

    switch(mode){
        case 0: 
            handle0(); 
            break; // 正确
        
        case 1: 
            handle1(); 
            return;
    }

Rule 2.10 — 循环条件必须可确定(Required)

  • 核心要求:循环条件必须最终可计算为布尔值

  • 示例

    while (counter < MAX_COUNT)  // 正确
    {
        counter++;
    }

Rule 2.11 — 循环计数器类型必须明确(Required)

  • 核心要求:循环变量类型必须固定宽度,防止溢出

  • 示例

    for (uint16_t i = 0; i < MAX; i++)
    {
        /* 正确 */
    }

Rule 2.12 — goto 禁止跳入或跳出条件块(Required)

  • 核心要求:goto 不允许跨条件块使用

  • 示例

    if (flag)
    {
        goto label; // 错误
    } 
    
    label: ;

Rule 2.13 — 逻辑表达式不允许副作用(Required)

  • 核心要求:条件表达式中禁止修改变量

  • 示例

    if (a++ > 10) // 错误
    {
    
    }
    
    if (a > 10)  // 正确
    {
        a++;
    }

Rule 2.14 — 条件中禁止使用魔法数字(Required)

  • 核心要求:条件判断中应使用命名常量或宏

  • 示例

    if (sensorValue > MAX_SENSOR_VAL)  // 推 荐
    {
    
    }

Rule 2.15 — 对指针判断必须显式与 NULL 比较(Required)

  • 核心要求if(ptr != NULL) 而不是 if(ptr)

  • 示例

    if (ptr != NULL)     /* 安全 */
    {
    
    }

Rule 2.16 — 条件中不允许使用可变位操作(Required)

  • 核心要求:避免在 if 中对位进行修改

  • 示例

    if ((flags |= 0x01)) // 错误
    {
    
    }

Rule 2.17 — 条件避免依赖实现定义行为(Required)

  • 核心要求:条件中避免使用未定义行为或依赖编译器实现

  • 示例

    if (sizeof(int) > 4) /* 正确 */
    {
    
    }

Rule 2.18 — 复杂条件应拆分为子表达式(Advisory)

  • 核心要求:条件过长或逻辑复杂时应拆分,提升可读性

  • 示例

    bool cond1 = (a>0 && b<100);
    bool cond2 = (c==5 && d!=0);
    
    if (cond1 || cond2)    /* 正确 */
    {
    
    }

三.循环控制(Rule 29–50)

本章节涵盖 22 条规则,核心目标是保证循环结构安全、可预测并易于维护,避免死循环、溢出、未定义行为或循环计数错误对 MCU 软件造成影响。

Rule 3.1 — 循环必须保证终止条件可达(Mandatory)

  • 核心要求:所有循环必须有明确可达的退出条件。

  • 示例

    for (uint16_t i=0; i<MAX_COUNT; i++)    /* 正确 */
    {
    
    }

Rule 3.2 — 循环计数器类型必须固定宽度(Mandatory)

  • 核心要求:循环计数器使用明确宽度类型(uint8_t/uint16_t/uint32_t)。

  • 示例

    for (uint8_t idx=0; idx<10; idx++) /* 正确 */
    {
    
    }

Rule 3.3 — 循环体内禁止修改循环变量外部依赖(Required)

  • 核心要求:循环条件依赖的变量不能在循环体内被意外修改

  • 示例

    uint16_t cnt = MAX;
    
    while (cnt>0)  // 正确
    {
        cnt--;
    }
    
    while (cnt>0) // 错误,循环逻辑被破坏
    {
        cnt = 0;
    }

Rule 3.4 — 禁止在循环条件中使用赋值(Required)

  • 核心要求:循环条件中禁止赋值操作

  • 示例

    while (flag = 1) // 错误
    {
    
    }
    
    while (flag == 1) // 正确
    {
    
    }

Rule 3.5 — 循环中避免使用魔法数字(Required)

  • 核心要求:循环条件和计数应使用常量或宏定义

  • 示例

    for (uint16_t i=0; i<MAX_RETRY; i++) /* 正确 */
    {
    
    }

Rule 3.6 — 避免无限循环,必要时加 Watchdog 或超时检测(Required)

  • 核心要求:长循环或等待循环应设计超时机制

  • 示例

    uint16_t timeout = 1000;
    
    while (!flag && timeout--)  // 正确,防止死循环
    {
    
    }

Rule 3.7 — 循环体必须使用花括号(Required)

  • 核心要求:所有 for/while/do 循环体使用 {} 包围

  • 示例

    for (i=0; i<10; i++)
    {
        doSomething();
    }

Rule 3.8 — 循环中禁止复杂表达式(Advisory)

  • 核心要求:条件表达式尽量简单,避免多层逻辑和副作用

  • 示例

    while ( ((a > 0) && (b < 100))
         || ((c == 5) && (d != 0)))     /* 拆分更易读 */
    {
    
    }

Rule 3.9 — 避免嵌套循环过深(Advisory)

  • 核心要求:嵌套循环深度应尽量 <=2,必要时拆分函数

  • 示例

    for(...)
    {
        for(...)
        {
            /* 深度 2 可接受 */
        }
    }

Rule 3.10 — 循环变量只在循环体内使用(Advisory)

  • 核心要求:减少循环外对循环变量依赖

  • 示例

    for (uint16_t i = 0; i < N; i++)
    {
        process(i);
    }

Rule 3.11 — do-while 循环条件必须明确(Required)

  • 核心要求:do-while 循环条件必须最终可为布尔值

  • 示例

    do
    {
        readSensor();
    } while (sensorReady != 0);

Rule 3.12 — 循环体内部禁止使用 goto 跳出循环(Required)

  • 核心要求:禁止跨循环块的 goto

  • 示例

    goto label; // 错误
    break;       // 正确跳出

Rule 3.13 — 避免循环中动态内存分配(Required)

  • 核心要求:循环中不允许调用 malloc/free 等,防止堆碎片或实时性下降

  • 示例

    for (...) /* 错误 */
    {
        ptr = malloc(...); 
    }

Rule 3.14 — 循环中禁止修改外部全局状态(Advisory)

  • 核心要求:避免循环对全局变量造成副作用,必要时使用局部副本

  • 示例

    uint16_t localVal = g_counter;
    
    for(...) 
    {
        localVal++;  // 安全
    }
    
    g_counter++;              // 警示

Rule 3.15 — 循环计数器避免超过类型范围(Required)

  • 核心要求:确保计数器不会溢出

  • 示例

    for (uint8_t i=0; i<300; i++)  // 错误,uint8_t 最大255,需要确认
    {
    
    }

Rule 3.16 — 循环退出条件必须可预测(Required)

  • 核心要求:循环退出条件不能依赖不可控硬件状态

  • 示例

    while (flagSensor) // 正确,flagSensor 定义明确
    {
    
    }

Rule 3.17 — 循环体函数调用注意执行时间(Advisory)

  • 核心要求:循环体内调用函数执行时间应可预测,避免阻塞 MCU 主循环

  • 示例

    for (i = 0; i < N; i++)
    {
        readSensor();   // 确认 readSensor 时间可控
    }

Rule 3.18 — 循环中禁止使用浮点变量(Required)

  • 核心要求:避免循环中使用浮点运算,MCU 资源有限且可能影响性能

  • 示例

    for (i = 0; i < N; i++)
    {
        float val = 0.1f; // 尽量避免
    }

Rule 3.19 — 循环计数器初始化必须明确(Mandatory)

  • 核心要求:循环变量必须在声明时初始化

  • 示例

    for (uint16_t i=0; i<N; i++)
    {
    
    }

Rule 3.20 — 循环条件避免依赖外部中断触发(Required)

  • 核心要求:循环条件不应依赖外部中断状态,必要时使用安全副本

  • 示例

    while (g_flag)
    {
        /* 安全读取局部副本 */
    }

Rule 3.21 — 避免循环体内函数多次修改同一全局变量(Advisory)

  • 核心要求:循环体内多函数访问同一全局变量应谨慎

  • 示例

    for (...) 
    {
        func1(); // func1/func2 不应同时修改同一全局变量
        func2();
    }

Rule 3.22 — 循环计数器尽量局部化(Advisory)

  • 核心要求:循环变量只在循环作用域内声明,减少外部依赖

  • 示例

    for (uint16_t i = 0; i < N; i++)
    {
    
    }

四.函数(Rule 51–80)

本章节涵盖 30 条规则,核心目标是确保函数设计清晰、安全、可维护,并避免因参数传递、返回值或副作用导致 MCU 软件异常。

Rule 4.1 — 每个函数应有明确的功能(Mandatory)

  • 核心要求:函数应实现单一功能,避免过多职责混合。

  • 示例

    void ReadSensor(void);       // 正确
    void ReadSensorAndCompute();  // 功能混合,应拆分

Rule 4.2 — 函数参数数量应控制(Advisory)

  • 核心要求:参数过多会降低可读性和可维护性,推荐 ≤4。

  • 示例

    void SetConfig(uint16_t mode, uint16_t speed); // 合理
    void SetConfig(uint16_t mode, uint16_t speed, uint16_t delay, uint16_t flag, uint16_t level); // 参数过多

Rule 4.3 — 参数类型应明确且固定宽度(Mandatory)

  • 核心要求:函数参数使用固定宽度类型,避免隐式类型转换

  • 示例

    void SetThreshold(uint16_t threshold); // 正确
    void SetThreshold(unsigned int threshold); // 不推荐

Rule 4.4 — 使用 const 修饰只读参数(Required)

  • 核心要求:函数中只读参数加 const,防止误修改

  • 示例

    void ProcessData(const uint8_t* data, uint16_t length);

Rule 4.5 — 避免返回指针指向局部变量(Mandatory)

  • 核心要求:禁止返回函数内局部变量地址

  • 示例

    uint8_t* GetBuffer(void)
    {
        static uint8_t buf[10];
        return buf; // 静态变量允许
    }

Rule 4.6 — 避免返回指针指向动态分配的局部对象(Mandatory)

  • 核心要求:避免返回临时 malloc 的地址,确保生命周期可控

  • 示例

    uint8_t* GetBufferSafe(void)
    {
        static uint8_t buf[10];
        return buf;
    }

Rule 4.7 — 函数应有明确返回值(Mandatory)

  • 核心要求:非 void 函数必须返回有效值

  • 示例

    int16_t ReadSensor(void){
        return sensorValue; // 正确
    }

Rule 4.8 — 避免函数内静态变量造成非线程安全(Required)

  • 核心要求:函数内部静态变量在多任务/ISR中可能非线程安全

  • 示例

    static uint16_t counter; // 使用需加锁或仅限单任务访问

Rule 4.9 — 函数长度应可控(Advisory)

  • 核心要求:函数行数宜短,便于维护和测试

  • 示例

    void Process(void) { /* ≤50 行为佳 */ }

Rule 4.10 — 避免函数副作用过多(Advisory)

  • 核心要求:函数尽量只修改自身作用域或传入参数

  • 示例

    void UpdateStatus(Status_t* s); // 安全
    void UpdateGlobalAndPeripheral(); // 副作用过多

Rule 4.11 — 函数名应清晰反映功能(Advisory)

  • 核心要求:函数名应描述其功能和作用域

  • 示例

    void ReadTemperature(void); // 清晰
    void DoIt(void);             // 不清晰

Rule 4.12 — 避免递归(Required)

  • 核心要求:MCU 栈有限,递归可能导致溢出

  • 示例

    void FuncA(void)
    {
        FuncA();   // 禁止
    }

Rule 4.13 — 函数参数不要依赖全局状态(Required)

  • 核心要求:函数应以参数为输入,减少对全局变量的依赖

  • 示例

    void Process(uint16_t value); // 安全
    void ProcessGlobal(void);      // 警示

Rule 4.14 — 函数中禁止魔法常量(Required)

  • 核心要求:硬编码数值应使用宏或 const 定义

  • 示例

    #define MAX_RETRY 10
    
    for (i = 0; i < MAX_RETRY; i++)
    {
    
    }

Rule 4.15 — 函数内变量初始化(Mandatory)

  • 核心要求:函数内局部变量必须初始化

  • 示例

    void Func(void){
        uint16_t cnt = 0;
    }

Rule 4.16 — 函数接口应保持一致性(Advisory)

  • 核心要求:接口参数顺序、类型风格在同类函数中统一

  • 示例

    void Sensor_SetThreshold(uint16_t t);
    void Sensor_GetValue(uint16_t* val);


Rule 4.17 — 避免函数依赖未定义行为(Required)

  • 核心要求:禁止在函数中使用未定义行为,如越界访问数组

  • 示例

    uint8_t arr[10];
    arr[10] = 0; // 错误

Rule 4.18 — 函数文档化(Advisory)

  • 核心要求:函数功能、参数、返回值、异常情况需文档化

  • 示例

    /**
     * @brief 读取传感器值
     * @param  sensorId 传感器编号
     * @return 传感器值
     */

Rule 4.19 — 避免函数过长生命周期副作用(Advisory)

  • 核心要求:函数尽量保持短生命周期,避免静态状态污染

  • 示例

    void InitModule(void); // 短生命周期,初始化后返回

Rule 4.20 — 函数内禁止中断敏感操作(Required)

  • 核心要求:函数内操作若可能被 ISR 干扰,需加保护

  • 示例

    uint16_t value;
    EnterCritical();
    value = g_counter;
    ExitCritical();

Rule 4.21 — 避免函数中使用全局指针(Required)

  • 核心要求:全局指针可能引发未定义行为

  • 示例

    void Process(uint16_t* ptr); // 避免指向全局指针

Rule 4.22 — 函数内禁止动态内存分配(Required)

  • 核心要求:MCU 循环调用 malloc/free 会破坏实时性

  • 示例

    void Func(void)
    {
        /* 避免 malloc/free */
    }

Rule 4.23 — 函数返回值应检查(Mandatory)

  • 核心要求:非 void 函数返回值必须被调用者使用或校验

  • 示例

    int16_t ret = ReadSensor();
    if(ret < 0){ HandleError(); }

Rule 4.24 — 避免函数内部副作用修改全局状态(Advisory)

  • :函数尽量只修改自身作用域或传入参数

  • 示例

    void UpdateStatus(Status_t* s);

Rule 4.25 — 函数尽量可重入(Required)

  • 核心要求:可重入函数在中断或多任务场景安全

  • 示例

    void Process(const uint8_t* data);

Rule 4.26 — 函数异常路径处理(Mandatory)

  • 核心要求:函数必须处理异常或返回错误码

  • 示例

    int16_t ReadSensor(uint8_t id);

Rule 4.27 — 避免函数内长时间阻塞(Required)

  • 核心要求:函数执行时间必须可控

  • 示例

    void Func(void){ /* 禁止长循环 */ }

Rule 4.28 — 函数内部循环控制遵循循环章节规则(Required)

  • 核心要求:循环体内函数遵循循环控制规则

  • 示例

    for (i = 0; i < N; i++)
    {
        ProcessData(); 
    }

Rule 4.29 — 函数内条件判断简明(Advisory)

  • 核心要求:避免复杂嵌套条件影响可读性

  • 示例

    if (a > 0)
    {
        if (b > 0)
        {
            /* 拆分函数 */
        }
    }

Rule 4.30 — 函数参数和返回值类型一致(Required)

  • 核心要求:接口类型必须清晰,避免隐式转换

  • 示例

    uint16_t GetValue(void); // 返回类型与使用方一致

五.条件控制(Rule 81–120)

本章节涵盖 40 条规则,核心目标是保证条件判断安全、清晰、可预测,避免因逻辑错误、未定义行为或边界问题导致 MCU 软件异常。

Rule 5.1 — 条件表达式必须明确(Mandatory)

  • 核心要求:条件判断中不要使用模糊或隐式转换

  • 示例

    if ((status & 0x01) != 0)  /* 明确 */
    {
    
    }
    
    if (status & 0x01)      /* 不推荐 */
    {
    
    }

Rule 5.2 — 避免赋值运算出现在条件中(Required)

  • 核心要求:条件判断中不要写成赋值表达式,避免逻辑错误

  • 示例

    if (x == 0)  // 正确
    {
    
    }
    
    if (x = 0)   // 错误
    {
    
    }

Rule 5.3 — 条件判断结果应为布尔类型(Advisory)

  • 核心要求:逻辑表达式应产生明确 true/false,避免非零整数混淆

  • 示例

    bool flag = (x > 0);

Rule 5.4 — 避免复杂嵌套条件(Required)

  • 核心要求:条件嵌套深度应控制,推荐 ≤3

  • 示例

    if (a > 0)
    {
        if (b > 0)
        {
            // 可接受
        }
    }

Rule 5.5 — 条件判断边界应清晰(Mandatory)

  • 核心要求:使用 >=、<= 等明确边界,避免 off-by-one 错误

  • 示例

    if ((index >= 0) && (index < ARRAY_SIZE))
    {
    
    }

Rule 5.6 — 避免重复条件(Required)

  • 核心要求:条件判断中不要重复计算相同表达式

  • 示例

    if ((x>0) && (x>0)) // 错误
    {
    
    }

Rule 5.7 — 避免条件依赖未定义行为(Mandatory)

  • 核心要求:条件中不能包含未定义行为,如溢出、除以 0

  • 示例

    if ((a / b) > 0) // 检查 b != 0
    {
    
    }

Rule 5.8 — 使用括号明确逻辑运算优先级(Required)

  • 核心要求:逻辑运算优先级不明时,必须加括号

  • 示例

    if ((a && b) || c) // 明确
    {
    
    }
    
    if (a && b || c)   // 易混淆
    {
    
    }

Rule 5.9 — 避免条件中函数副作用(Required)

  • 核心要求:条件判断中调用可能修改全局状态的函数需谨慎

  • 示例

    if (GetFlag())  // 安全
    { 
    
    }
    
    if (IncrementCounter())  // 不推荐
    {
    
    }

Rule 5.10 — 条件表达式应可测试(Advisory)

  • 核心要求:避免复杂常量组合或无法覆盖的条件分支

  • 示例

    if ((x > 0) && (y < 100))  // 可测试
    {
    
    }
    
    if ((x > 0) && (y < MAX_UINT16)) // 难以覆盖
    {
    
    }

Rule 5.11 — 避免空条件语句(Required)

  • 核心要求:if/else 后禁止空语句

  • 示例

    if(flag) { DoSomething(); } // 正确
    if(flag);                   // 错误

Rule 5.12 — 条件判断逻辑应简明(Advisory)

  • 核心要求:避免复杂组合条件,应拆分成多个判断

  • 示例

    if ((a > 0) && (b > 0) && (c < 10)) /* 可拆分 */ 
    {
    
    }

Rule 5.13 — switch 必须包含 default(Mandatory)

  • 核心要求:确保未列出的枚举值有处理路径

  • 示例

    switch(mode){
        case MODE_OFF: ...
        case MODE_ON: ...
        default: HandleError(); 
    }

Rule 5.14 — switch case 不得重复(Required)

  • 核心要求:每个 case 值唯一

  • 示例

    case 0: ...
    case 1: ...
    // case 1: ... 错误


Rule 5.15 — switch case 应覆盖所有枚举(Advisory)

  • 核心要求:对枚举型变量,应确保每个值有 case

  • 示例

    typedef enum { RED, GREEN, BLUE } Color;
    
    switch(c)
    {
        case RED: ... 
        case GREEN: ... 
        case BLUE: ... 
    }

Rule 5.16 — switch 内禁止 fall-through(Required)

  • 核心要求:禁止不明确的 case 穿透,必须使用 break 或注释

  • 示例

    case 0: 
        DoSomething();
        break;
    
    case 1: 
        DoSomethingElse(); 
        break;

Rule 5.17 — 条件表达式应避免副作用(Required)

  • 核心要求:避免在条件中修改变量

  • 示例

    if ((x++) > 0) // 错误
    { 
    
    } 
    
    if(x > 0)
    {
        x++;  // 安全
    } 

Rule 5.18 — 使用逻辑常量时显式比较(Required)

  • 核心要求:不要直接使用数字替代布尔值

  • 示例

    if (flag == true)
    {
    
    }

Rule 5.19 — 避免条件中多层函数调用(Advisory)

  • 核心要求:复杂条件可拆成中间变量

  • 示例

    bool tmp = CheckA() && CheckB();
    
    if (tmp)
    {
    
    }

Rule 5.20 — 避免条件短路副作用(Required)

  • 核心要求:短路逻辑中不应包含修改操作

  • 示例

    if ((flagA && (counter++ > 0))) // 错误
    {
    
    }

Rule 5.21 — 条件表达式避免魔法数字(Required)

  • 核心要求:使用宏或 const 定义常量

  • 示例

    #define MAX_RETRY 10
    if(count < MAX_RETRY) { }

Rule 5.22 — 避免条件判断依赖未初始化变量(Mandatory)

  • 核心要求:使用前必须初始化

  • 示例

    uint8_t flag;
    
    if (flag)  // 错误
    {
    
    }

Rule 5.23 — 条件判断分支必须可达(Required)

  • 核心要求:保证逻辑可执行,避免死代码

  • 示例

    if (a>0)
    {
        ...
    }
    else if (a>0)   // 错误
    {
        ...
    }
    else
    {
    
    }

Rule 5.24 — 条件判断中避免强制类型转换(Required)

  • 核心要求:不要在条件判断中做强制转换

  • 示例

    if ((int)x > 0)  // 不推荐
    {
    
    }

Rule 5.25 — 条件逻辑应可单元测试(Advisory)

  • 核心要求:条件判断应拆分成可测试的单元

  • 示例

    bool isValid = ((x > 0) && (y < 100));
    
    if (isValid)
    {
    
    }

Rule 5.26 — 条件中避免函数返回未定义值(Mandatory)

  • 核心要求:函数必须返回有效值

  • 示例

    if (GetValue() > 0)  // 安全前提:GetValue 返回有效值
    {
    
    }

Rule 5.27 — 条件表达式应可读性良好(Advisory)

  • 核心要求:避免长表达式堆叠

  • 示例

    bool result = (a>0) && (b>0) && (c<10);

Rule 5.28 — 避免条件嵌套过深(Required)

  • 核心要求:嵌套超过 3 层应拆分函数

  • 示例

    if (a > 0)
    {
        if (b > 0)
        {
            if (c > 0)  // 可拆分
            {
                ...
            }
        }
    }

Rule 5.29 — 条件判断中避免全局状态依赖(Required)

  • 核心要求:尽量使用参数,减少全局变量依赖

  • 示例

    if (GetFlag(param))
    {
    
    }

Rule 5.30 — 条件表达式结果类型一致(Required)

  • 核心要求:确保逻辑表达式返回 bool 或明确整数类型

  • 示例

    bool flag = (x > 0);

Rule 5.31 — 避免条件表达式重复计算(Required)

  • 核心要求:复杂表达式应保存到中间变量

  • 示例

    bool tmp = (x > 0) && (y < 10);
    
    if (tmp)
    {
    
    }

Rule 5.32 — 避免条件中使用非标准运算符(Required)

  • 核心要求:禁止使用非标准、平台相关运算符

  • 示例

    if (x ** 2 > 10) // 错误
    {
    
    } 

Rule 5.33 — 条件逻辑应保持简洁(Advisory)

  • 核心要求:避免连续多个逻辑运算符堆叠

  • 示例

    if(a && b && c) { }

Rule 5.34 — 条件判断注释应清晰(Advisory)

  • 核心要求:复杂条件应加注释

  • 示例

    if ((a > 0) && (b < 10))  /* 检查输入有效性 */  
    {
    
    }

Rule 5.35 — 条件表达式避免未定义宏(Required)

  • 核心要求:宏值必须有效

  • 示例

    #define FLAG_VALID 1
    
    if (flag == FLAG_VALID)
    {
    
    }

Rule 5.36 — 避免条件中使用静态变量(Required)

  • 核心要求:条件中静态变量可能引起副作用

  • 示例

    static uint8_t counter;
    
    if (counter > 0)
    {
    
    }

Rule 5.37 — 条件中避免修改数组索引(Required)

  • 核心要求:条件表达式中不要修改数组索引

  • 示例

    if (arr[i++] > 0)  // 错误
    {
    
    }

Rule 5.38 — 条件判断中避免调用阻塞函数(Required)

  • 核心要求:MCU 实时性要求,不允许条件判断调用阻塞函数

  • 示例

    if (DelayMs(10)) // 不允许
    {
    
    }

Rule 5.39 — 条件表达式结果应与期望类型匹配(Required)

  • 核心要求:避免布尔与整数混用导致逻辑错误

  • 示例

    bool flag = (x != 0);

Rule 5.40 — 条件判断应覆盖边界场景(Mandatory)

  • 核心要求:确保条件判断覆盖上下限、边界值和特殊值

  • 示例

    if ((index >= 0) && (index < ARRAY_SIZE))
    {
    
    }

六.循环控制(Rule 121–143)

本章节涵盖 23 条规则,核心目标是保证循环结构安全、可预测、边界明确,避免死循环、越界和未定义行为,提高 MCU 程序稳定性。

Rule 6.1 — 循环必须可控(Mandatory)

  • 核心要求:所有循环必须有明确终止条件

  • 示例

    for (uint16_t i = 0; i < MAX_COUNT; i++)  /* 安全 */ 
    {
    
    }

Rule 6.2 — 避免无限循环(Required)

  • 核心要求:禁止没有出口的 while 或 for 循环

  • 示例

    while(1) 
    {
        DoSomething();
        break;  // 必须有退出机制
    }

Rule 6.3 — 循环边界应明确(Mandatory)

  • 核心要求:循环计数器类型与循环条件应匹配,避免溢出

  • 示例

    for (uint8_t i = 0; i<10; i++)
    {
    
    }

Rule 6.4 — 循环中避免修改循环计数器(Required)

  • 核心要求:禁止在循环体中修改计数器,保证可预测性


Rule 6.5 — 循环条件表达式应简洁(Advisory)

  • 核心要求:避免复杂运算,易于理解和测试

  • 示例

    for (i = 0; i < MAX; i++)  // 简单清晰
    {
    
    } 

Rule 6.6 — 避免循环中出现副作用(Required)

  • 核心要求:循环条件中不应有影响外部状态的表达式

  • 示例

    while (UpdateCounter())  // 不推荐
    {
    
    } 

Rule 6.7 — 循环变量类型匹配(Mandatory)

  • 核心要求:循环计数器应与数据结构索引类型一致,避免越界

  • 示例

    for (size_t i = 0; i < ARRAY_SIZE; i++)
    {
    
    }

Rule 6.8 — 避免循环体空语句(Required)

  • 核心要求:循环体不可为空

  • 示例

    for (i = 0; i < MAX; i++); // 错误
    {
    
    }

Rule 6.9 — 循环中避免复杂条件(Advisory)

  • 核心要求:条件复杂度高应拆分

  • 示例

    for (i = 0; ((i < MAX) && (arr[i] != 0)); i++)   // 简化或拆分
    {
    
    }

Rule 6.10 — 避免循环变量溢出(Mandatory)

  • 核心要求:计数器必须在安全范围内,考虑类型最大值

  • 示例

    for (uint8_t i = 0; i < 255; i++) // 注意循环上限
    {
    
    } 

Rule 6.11 — 循环退出条件应可靠(Required)

  • 核心要求:防止条件依赖未初始化变量或外部异常

  • 示例

    while (flag)  // flag 必须初始化
    {
    
    }

Rule 6.12 — 避免循环体中函数副作用(Required)

  • 核心要求:循环条件或体中调用函数不应破坏全局状态

  • 示例

    for (i = 0;i < N; i++)  // 函数内自行管理状态
    {
        ProcessData();
    }

Rule 6.13 — 循环中避免访问越界数组(Mandatory)

  • 核心要求:严格检查索引边界

  • 示例

    for (i = 0; i < ARRAY_SIZE; i++)
    {
        data[i] = 0;
    }

Rule 6.14 — 循环体应易读(Advisory)

  • 核心要求:避免嵌套过深,适当拆分

  • 示例

    for (i = 0; i < N; i++)
    {
        if (flag)
        {
            Process(i);
        }
    }

Rule 6.15 — 避免循环嵌套过深(Required)

  • 核心要求:嵌套深度 > 3 应拆分函数

  • 示例

    for(...)
    {
        for(...)
        {
            for(...)
            {
                ...
            }
        }
    }

Rule 6.16 — 循环中变量应尽量局部(Advisory)

  • 核心要求:循环计数器及临时变量应在循环内部定义

  • 示例

    for (int i = 0; i < N; i++)
    {
        int tmp= i * 2;
    }

Rule 6.17 — 循环中避免使用浮点(Required)

  • 核心要求:嵌入式 MCU 循环中浮点可能带来性能问题

  • 示例

    for (int i = 0; i < N; i++)
    {
        int val = i * 2;  // 避免 float
    }

Rule 6.18 — 循环条件应避免魔法数字(Required)

  • 核心要求:使用宏或 const 定义

  • 示例

    for (int i = 0; i < MAX_COUNT; i++)
    {
    
    }

Rule 6.19 — 循环体应避免未使用变量(Advisory)

  • 核心要求:清理未使用变量,保持代码整洁


Rule 6.20 — 循环变量不可修改其他全局状态(Required

  • 核心要求:保证循环可预测性


Rule 6.21 — 循环中避免调用阻塞函数(Required)

  • 核心要求:嵌入式实时任务循环禁止阻塞调用


Rule 6.22 — 循环中避免调用可能出错的函数(Required)

  • 核心要求:函数必须保证安全,避免循环中异常中断


Rule 6.23 — 循环变量结果应可单元测试(Advisory)

  • 核心要求:循环逻辑应拆分可测试模块

  • 示例

    for (int i = 0; i < N; i++)
    {
        ProcessStep(i);
    }

七.MISRA C:2012 指令(Directives)

Directive 1 — 可移植性检查

  • 确保代码在不同编译器和硬件平台上具有可移植性。

Directive 2 — 头文件包含策略

  • 每个头文件应自包含(即一个头文件可以独立被包含到任何 C 文件中而不会出错,不依赖其他头文件提前包含)。
  • 防止重复包含(使用 include guards 或 #pragma once)。

Directive 3 — 注释使用规范

  • 注释必须清晰、准确,解释“为什么”而非“做什么”。避免误导性或过时注释。

Directive 4 — 代码可读性和结构

  • 代码应模块化、清晰、易读。控制结构应尽量简单,减少嵌套层次。

Directive 5 — 预处理器指令使用

  • 仅在必要时使用 #define#if 等预处理器指令。避免宏滥用。

Directive 6 — 编译器依赖性检查

  • 避免依赖特定编译器扩展或行为。

Directive 7 — 可测试性

  • 代码应支持静态分析和单元测试。
  • 功能模块应独立、接口清晰。


Directive 8 — 错误处理策略

  • 对可能出现的错误情况进行明确处理(如返回码、异常处理)。

Directive 9 — 函数接口设计

  • 函数参数、返回值、全局依赖清晰定义。
  • 避免副作用。

Directive 10 — 变量和类型使用

  • 明确定义变量类型,避免隐式转换。
  • 遵循命名规范。

Directive 11 — 循环和控制结构

  • 避免复杂嵌套和不可预测循环。
  • 控制结构尽量简单明了。

Directive 12 — 宏定义和常量

  • 宏仅用于必要场景,优先使用常量、枚举或 inline 函数。

Directive 13 — 静态分析工具兼容性

  • 编写代码时考虑静态分析工具的检查能力。

Directive 14 — 库函数使用策略

  • 避免使用不可预测或非标准库函数。优先使用标准、可移植的库函数。

Directive 15 — 对外接口文档规范

  • 所有对外接口(函数、模块、通信协议)必须有明确文档说明。

Directive 16 — 安全与关键功能的审查

  • 安全关键功能必须经过专门审查,设计和实现遵循高可靠性标准。

八.补充内容

8.1 C语言空格使用指南

1)空格使用规则总结

类别 规则描述
1. 关键字 if, for, while, do, switch, case 等关键字后必须加一个空格
2. 函数名 函数名与其后的左括号 ( 之间不加任何空格
3. 括号 括号(圆括号 (), 方括号 [])的内侧不加空格,与内部内容紧贴
4. 大括号 通常左大括号 { 放在语句末尾或新行,右大括号 } 单独一行。内部语句缩进
5. 二元运算符 所有二元运算符(算术、逻辑、比较、位操作)两边都必须加空格
6. 赋值运算符 赋值运算符(=, +=, -= 等)两边都必须加空格
7. 一元运算符 一元运算符(!, ~, ++, --, *(指针), &(取地址))与它的操作数之间不加空格
8. 逗号 逗号 , 后面必须加一个空格,前面不加空格。
9. 分号 分号 ; 前面不加空格(for 循环中的三个部分除外)。语句结束后换行或跟随空格
10. 结构体/指针访问 成员访问运算符 . 和 -> 周围不加空格。

2)代码示例

#include <stdio.h>

int main(void)
{
    int a = 5;
    int b = 10;
    int c = 15;
    int result = 0;
    int* ptr = &a; // 一元运算符 * 和 & 紧贴操作数

    // 条件控制
    if ((a > 0) && (b < 20) || (c == 15))
    {
        result = 1;
    }

    // 循环控制
    for (int i = 0; i < 10; i++) // for循环中的分号后加空格
    {
        result += i; // 二元运算符 += 两边加空格
    }

    while ((a < 10) && (b > 5))
    {
        a++; // 一元运算符 ++ 紧贴操作数
    }

    do
    {
        b--;
    } while ((b > 0) && (a < 20));

    // 赋值和算术运算
    a = b + c;
    result = result + (a - (b * c) / 2); // 通过括号和空格明确优先级

    // 逻辑与位运算
    if ((a > 0) && !(b == c) || (c != 0)) // 一元运算符 ! 紧贴操作数
    {
        result = 100;
    }
    int flags = (a & 0x0F) << 4; // 位运算符两边同样加空格

    // 函数调用与结构体访问(示例)
    printf("Result: %d\n", result); // 函数名后无空格,参数逗号后有空格

    return 0;
}

8.2 复杂条件多行分解规范

核心原则是:清晰展示逻辑结构,让每一个逻辑单元(&& / ||)都显而易见

8.2.1 方法一:运算符置于行首

这是目前最受推崇的写法,尤其是在安全关键领域。它将逻辑运算符放在每一行的开头,就像列表一样,非常清晰地展示了逻辑的延续关系。

  • 规则

    • 将整个条件表达式与 if 放在同一行开始。
    • 换行后,将逻辑运算符(&&, ||)作为新行的开头。
    • 所有后续行的逻辑运算符与第一行 if 关键字的位置对齐。
    • 使用括号将不同优先级的逻辑分组,并进行适当的缩进。
  • 示例

    if ((system_state == STATE_READY)
        && (network_status == CONNECTED)
        && ((request_type == GET_REQUEST) || (request_type == POST_REQUEST))
        && (payload_size < MAX_PAYLOAD_SIZE)
        && !(error_flags & FATAL_ERROR_MASK))
    {
        process_request();
    }
    
    // 嵌套分组的情况,使用额外缩进
    if ( (a > 0)
        && ( (b_is_valid && (b < MAX_B)) // 嵌套组,多缩进一层
            || (c_is_ready && (c != LAST_VALUE)) )
        && (d == 0))
    {
        do_work();
    }
  • 优点

  • 易于阅读:眼睛只需向左扫视就能看到所有的逻辑连接词。
  • 易于调试:注释掉任何一行(以 && 或 || 开头的行)都非常容易,不会破坏语法。
  • 易于修改:增删条件时,git diff 的变更记录会更清晰,通常只涉及一行的变化。

8.2.2 方法二:运算符置于行尾

这是一种比较传统的写法,但现在较少被推荐,尤其是在规范严格的项目中。

  • 规则

  • 将逻辑运算符(&&||)放在上一行的末尾。
  • 新行的内容与第一行条件表达式的起始位置对齐。
  • 示例

    if ((system_state == STATE_READY) &&
        (network_status == CONNECTED) &&
        ((request_type == GET_REQUEST) || (request_type == POST_REQUEST)) &&
        (payload_size < MAX_PAYLOAD_SIZE) &&
        !(error_flags & FATAL_ERROR_MASK))
    {
        process_request();
    }
  • 缺点

  • 可读性稍差:逻辑运算符容易被忽略,因为它们“隐藏”在行尾。
  • 难以修改和调试:如果要注释掉一个条件,必须小心地处理前一行末尾的运算符,否则会引发语法错误。

8.2.3 方法三:使用临时变量

当条件极其复杂,甚至包含函数调用和大量计算时,最好的分解方法不是换行,而是将条件分解为多个有意义的布尔变量

  • 规则

    • 将子条件计算并赋值给一个命名清晰的布尔变量。
    • 在 if 语句中使用这些变量进行组合。
  • 示例

    bool is_system_ready = (system_state == STATE_READY);
    bool is_network_ok = (network_status == CONNECTED);
    bool is_valid_request = (request_type == GET_REQUEST) || (request_type == POST_REQUEST);
    bool is_payload_valid = (payload_size < MAX_PAYLOAD_SIZE);
    bool has_no_fatal_errors = !(error_flags & FATAL_ERROR_MASK);
    
    // 主判断条件变得非常简单、自解释
    if (is_system_ready
        && is_network_ok
        && is_valid_request
        && is_payload_valid
        && has_no_fatal_errors)
    {
        process_request();
    }
  • 优点

  • 极致的可读性:变量名本身就是注释,清楚地说明了每个条件的目的。
  • 便于调试:可以在调试器中轻松查看每个中间变量的值,快速定位哪个子条件不满足。
  • 代码复用:这些变量可能在代码的其他地方也会被用到。
  • 简化主逻辑:使 if 语句变得非常简洁,易于理解。

想了解更多嵌入式技术知识,请点击阅读我的其他文章

烟花的文章链接集合-CSDN博客

如果你觉得内容对您有帮助,别忘了点赞、收藏和分享支持下哦

Logo

openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。

更多推荐