目录

引言

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 10for (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 1if (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博客

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/94802.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/94802.shtml
英文地址,请注明出处:http://en.pswp.cn/bicheng/94802.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Custom SRP - Shadow Masks

截图展示的是:近处实时阴影,远处烘焙阴影1 Baking Shadows阴影让场景更具层次感和真实感,但是实时阴影渲染距离有限,超出阴影距离的世界由于没有阴影显得很“平”.烘焙的阴影不会受限于阴影距离,可以与实时阴影结合解决该问题:最大阴影距离之内使用实时阴影最大阴影距离之外用烘…

Python爬虫实战:研究spidermonkey库,构建电商网站数据采集和分析系统

1 引言 1.1 研究背景 互联网数据已成为商业决策、学术研究的核心资源,网络爬虫作为数据获取的主要工具,在静态网页时代发挥了重要作用。然而,随着 AJAX、React、Vue 等技术的广泛应用,超过 70% 的主流网站采用 JavaScript 动态生成内容(如商品列表滚动加载、评论分页加载…

智能驾驶规划技术总结

前言 本文主要对智能驾驶规划技术相关知识进行初步探究和总结&#xff0c;以加深理解&#xff0c;及方便后续学习过程中查漏补缺。 分层规划策略 寻径 A*算法 概念 节点&#xff1a;网格化后的每一个最小单元父节点&#xff1a;路径规划中用于回溯的节点列表&#xff1a;需要不…

05 网络信息内容安全--对抗攻击技术

1 课程内容 网络信息内容获取技术网络信息内容预处理技术网络信息内容过滤技术社会网络分析技术异常流量检测技术对抗攻击技术 2 对抗攻击概述 2.1 对抗攻击到底是啥&#xff1f; 咱们先举个生活例子&#xff1a; 你平时看苹果能认出来 —— 红颜色、圆溜溜、带个小揪揪。但如果…

【FPGA】VGA显示-贪吃蛇

这个项目实现了一个完整的贪吃蛇游戏&#xff0c;使用Verilog HDL在FPGA上构建。项目包含了VGA显示控制、按键消抖处理、游戏逻辑和图形渲染等多个模块&#xff0c;展示了数字逻辑设计的综合应用。 项目概述 该设计使用硬件描述语言实现了经典贪吃蛇游戏的所有核心功能&#…

从PostgreSQL到人大金仓(KingBase)数据库迁移实战:Spring Boot项目完整迁移指南

&#x1f4d6; 前言 在国产化浪潮的推动下&#xff0c;越来越多的企业开始将数据库从国外产品迁移到国产数据库。本文将以一个真实的Spring Boot项目为例&#xff0c;详细介绍从PostgreSQL迁移到人大金仓&#xff08;KingBase&#xff09;数据库的完整过程&#xff0c;包括遇到…

Docker 入门指南:从基础概念到常见命令及高级工具详解

Docker 入门指南&#xff1a;从基础概念到常见命令及高级工具详解 大家好&#xff01;今天我们来聊聊 Docker 这个强大的容器化工具。如果你是一个开发者、运维工程师&#xff0c;或者只是对云计算和容器技术感兴趣的人&#xff0c;Docker 绝对值得你深入了解。它可以帮助你轻松…

Redis数据持久化——RDB快照和Aof日志追加

Redis数据持久化数据持久化&#xff1a;将内存中的数据保存到磁盘中。作用&#xff1a;让Redis服务重启后可以恢复之前的数据。一、Redis数据持久化的方式&#xff1a;RDB&#xff08;快照&#xff09;&#xff1a;将内存中Redis缓存的所有数据&#xff0c;都以二进制字符串的方…

浅聊达梦数据库物理热备的概念及原理

达梦数据库&#xff08;DM Database&#xff09;的物理热备份&#xff0c;核心是在数据库不中断业务&#xff08;联机&#xff09; 的前提下&#xff0c;通过对数据库物理文件&#xff08;如数据文件、控制文件、日志文件等&#xff09;的增量或全量复制&#xff0c;实现数据备…

C++ 中 ::(作用域解析运算符)的用途

C 中 ::&#xff08;作用域解析运算符&#xff09;的应用场景详解 在 C 中&#xff0c;:: 被称为 作用域解析运算符&#xff08;Scope Resolution Operator&#xff09;&#xff0c;用于明确指定某个名字&#xff08;变量、函数、类型等&#xff09;所属的命名空间或类作用域&a…

鸿蒙中CPU活动分析:CPU分析

1 CPU分析的核心概念与重要性 CPU活动分析&#xff08;CPU Profiling&#xff09;是性能优化的核心手段&#xff0c;它通过测量代码执行时间&#xff0c;帮助开发者定位性能瓶颈。应用的响应速度直接影响用户体验&#xff0c;过长的加载时间或卡顿会导致用户流失 1.1 为什么C…

十大经典 Java 算法解析与应用

在 Java 开发的世界里&#xff0c;算法就如同构建大厦的基石&#xff0c;它们支撑着各种复杂应用的高效运行。无论是处理海量数据的排序&#xff0c;还是在庞大结构中精准查找信息&#xff0c;合适的算法都能大幅提升程序的性能。接下来&#xff0c;我们将深入解析十大经典的 J…

从感知机到大模型:神经网络的全景解析与实践指南

从感知机到大模型&#xff1a;神经网络的全景解析与实践指南在当今 AI 时代&#xff0c;我们身边的每一个智能应用 —— 从手机里的人脸识别、语音助手&#xff0c;到聊天机器人 ChatGPT、图像生成工具 MidJourney&#xff0c;再到自动驾驶的环境感知系统 —— 背后都离不开一个…

核心篇(下):Transformer 架构详解(程序员视角・实战版)

在上一篇 NLP 预处理文章中&#xff0c;你已经掌握了 “文本→向量” 的转化流程&#xff0c;解决了 DashScope Tokenizer 的调用问题。但此时你可能会问&#xff1a;“这些向量输入模型后&#xff0c;大模型是如何理解长文本语义的&#xff1f;比如‘小明告诉小红&#xff0c;…

FreeRTOS学习笔记(四):任务执行与切换

第一部分&#xff1a;FreeRTOS 任务是如何执行的&#xff1f; FreeRTOS 是一个抢占式的实时操作系统内核。其任务执行遵循一个核心原则&#xff1a;调度器&#xff08;Scheduler&#xff09;总是选择当前处于“就绪态”&#xff08;Ready&#xff09;的最高优先级任务来运行。 …

区块链技术探索与应用:从密码学奇迹到产业变革引擎

&#x1f31f; Hello&#xff0c;我是蒋星熠Jaxonic&#xff01; &#x1f308; 在浩瀚无垠的技术宇宙中&#xff0c;我是一名执着的星际旅人&#xff0c;用代码绘制探索的轨迹。 &#x1f680; 每一个算法都是我点燃的推进器&#xff0c;每一行代码都是我航行的星图。 &#x…

如何监控和调优JVM的内存使用情况?

监控和调优 JVM 内存使用是保障 Java 应用稳定性和性能的核心手段&#xff0c;需要结合监控工具、关键指标分析和针对性调优策略。以下是具体的实施方法&#xff1a;一、JVM 内存监控&#xff1a;工具与核心指标监控的目标是掌握内存使用趋势、GC 行为、线程状态等&#xff0c;…

把用户输进来的明文密码做一层 MD5 哈希

这一行干的就是&#xff1a;把用户输进来的明文密码先做一层 MD5 哈希&#xff0c;再把得到的 32 位十六进制字符串存到变量 password 里。 逐段拆开&#xff1a;password.getBytes() 把字符串转成字节数组&#xff0c;MD5 算法只能对字节/字节数组做运算。DigestUtils.md5Dige…

jeecg-boot3.7.0对接钉钉登录(OAuth2.0)

当前的jeecg-boot 是3.7.0前端问题&#xff1a;1.前端的路由vue-router的版本需要固定死。要不然会报page_not_found router the same.这种奇奇怪怪的问题。 就是把package.json的“^”&#xff0c;这个符号&#xff0c;删掉。&#xff08;或者全局搜索&#xff0c;这个page no…

【C#】获取不重复的编码(递增,非GUID)

获取不重复的编码&#xff1a;从原始实现到高效优化本文针对软件开发中“为新对象分配唯一编码”的常见需求&#xff0c;以C#通信设备管理场景为例&#xff0c;从原始代码分析入手&#xff0c;逐步讲解基于LINQ和哈希集合的优化方案&#xff0c;帮助开发者理解不同场景下的最佳…