目录

MISRA C 2023 规范

MISRA C 2023 规范

https://ww2.mathworks.cn/help/bugfinder/misra-c-2023-reference.html?lang=en


导则

实现相关

[导则 1.1] 程序输出所依赖的任何实现定义行为,均应形成文档并被充分理解。


编译与构建

[导则 2.1] 所有源文件的编译过程不应产生任何编译错误。


代码设计

[导则 4.1] 应最大限度降低运行时故障的发生概率。

[导则 4.3] 汇编语言应进行封装与隔离。

[导则 4.4] 不应将代码段 “注释掉”。

[导则 4.5] 同一命名空间中、可见范围存在重叠的标识符,在字形上应无歧义。

[导则 4.6] 宜使用指明了大小和符号性的 typedef 类型,替代基础数值类型。

[导则 4.7] 若函数返回错误信息,则应对该错误信息进行检查。

[导则 4.8] 若指向结构体或联合体的指针在单个翻译单元内从未被解引用,则该对象的实现宜对外隐藏。

[导则 4.9] 在函数与类函数宏可互换使用的场景下,宜优先使用函数。

[导则 4.10] 应采取预防措施,避免头文件内容被多次包含。

[导则 4.11] 应对传递给库函数的数值的有效性进行检查。

[导则 4.12] 不应使用动态内存分配。

[导则 4.13] 用于对资源执行操作的函数,应按合理的顺序调用。

[导则 4.14] 应对从外部来源接收的数值的有效性进行检查。

[导则 4.15] 浮点表达式的计算过程,不应导致无穷大与非数值(NaN)的生成未被检测。


并发相关考量

[导则 5.1] 线程之间不应存在数据竞争。

[导则 5.2] 线程之间不应出现死锁。

[导则 5.3] 不应进行动态线程创建。


规则

标准 C 语言环境

[规则 1.1] 程序不得违反标准 C 语言的语法与约束,且不得超出编译器实现的翻译限制。

[规则 1.2] 不宜使用语言扩展特性。

[规则 1.3] 程序中不应出现未定义行为或关键的未明确行为。

[规则 1.4] 不应使用新兴语言特性。

[规则 1.5] 不应使用过时语言特性。


未使用代码

[规则 2.1] 项目中不应包含不可达代码。

[规则 2.2] 项目中不应包含死代码。

[规则 2.3] 项目中不宜包含未使用的类型声明。

[规则 2.4] 项目中不宜包含未使用的标签声明。

[规则 2.5] 项目中不宜包含未使用的宏定义。

[规则 2.6] 函数中不宜包含未使用的标签声明。

[规则 2.7] 函数中不宜包含未使用的参数。

[规则 2.8] 项目中不宜包含未使用的对象定义。


注释

[规则 3.1] 注释内部不得使用 /*// 字符序列。

[规则 3.2] // 单行注释中不得使用行拼接。


字符集与词法约定

[规则 4.1] 八进制和十六进制转义序列必须有明确终止。

[规则 4.2] 不宜使用三字符序列。


标识符

[规则 5.1] 外部标识符必须具备唯一性。

[规则 5.2] 在同一作用域和命名空间中声明的标识符必须具备唯一性。

[规则 5.3] 内层作用域中声明的标识符,不得隐藏外层作用域中声明的同名标识符。

[规则 5.4] 宏标识符必须具备唯一性。

[规则 5.5] 标识符不得与宏名称重名。

[规则 5.6] typedef 名称必须是唯一标识符。

[规则 5.7] 标签名必须是唯一标识符。

[规则 5.8] 定义了具备外部链接属性的对象或函数的标识符,必须具备唯一性。

[规则 5.9] 定义了具备内部链接属性的对象或函数的标识符,宜具备唯一性。


类型

[规则 6.1] 位域只能使用合规的类型进行声明。

[规则 6.2] 单比特命名位域不得使用有符号类型。

[规则 6.3] 位域不得声明为联合体的成员。


字面量与常量

[规则 7.1] 不应使用八进制常量。

[规则 7.2] 所有以无符号类型表示的整型常量,都必须添加 uU 后缀。

[规则 7.3] 字面量后缀中不得使用小写字母 l

[规则 7.4] 除非对象的类型为 “指向 const 限定的 char 类型的指针”,否则不得将字符串字面量赋值给该对象。

[规则 7.5] 整型常量宏的参数必须使用合规的形式。

[规则 7.6] 不得使用最小宽度整型常量宏的短整型变体。


声明与定义

[规则 8.1] 必须显式指定类型。

[规则 8.2] 函数类型必须采用带命名参数的原型形式。

[规则 8.3] 同一对象或函数的所有声明,必须使用相同的名称与类型限定符。

[规则 8.4] 定义具备外部链接属性的对象或函数时,必须有一个兼容的声明处于可见状态。

[规则 8.5] 外部对象或函数必须在且仅在一个文件中声明一次。

[规则 8.6] 具备外部链接属性的标识符,必须有且仅有一个外部定义。

[规则 8.7] 若函数和对象仅在单个翻译单元中被引用,则不宜定义为具备外部链接属性。

[规则 8.8] 所有具备内部链接属性的对象和函数的声明,都必须使用 static 存储类型说明符。

[规则 8.9] 若对象的标识符仅在单个函数中出现,则宜在块作用域中声明该对象。

[规则 8.10] 内联函数必须使用 static 存储类型说明符声明。

[规则 8.11] 声明具备外部链接属性的数组时,宜显式指定其大小。

[规则 8.12] 枚举列表中,隐式赋值的枚举常量的值必须具备唯一性。

[规则 8.13] 只要可行,指针就应指向 const 限定的类型。

[规则 8.14] 不应使用 restrict 类型限定符。

[规则 8.15] 带有显式对齐规范的对象的所有声明,必须指定相同的对齐要求。

[规则 8.16] 对象声明中不宜出现零值对齐规范。

[规则 8.17] 单个对象声明中最多只能出现一个显式对齐说明符。


初始化

[规则 9.1] 自动存储期的对象,在赋值前不得读取其值。

[规则 9.2] 聚合体或联合体的初始化器必须包裹在大括号中。

[规则 9.3] 数组不得进行部分初始化。

[规则 9.4] 对象的元素不得被多次初始化。

[规则 9.5] 使用指定初始化器对数组对象进行初始化时,必须显式指定数组的大小。

[规则 9.6] 使用链式指定符的初始化器中,不得包含无指定符的初始化项。

[规则 9.7] 原子对象在被访问前,必须进行合规的初始化。


基本类型模块

[规则 10.1] 操作数不得使用不合规的基本类型。

[规则 10.2] 本质为字符类型的表达式,不得在加减运算中违规使用。

[规则 10.3] 表达式的值不得赋值给基本类型更窄、或基本类型类别不同的对象。

[规则 10.4] 执行通常算术转换的运算符的两个操作数,必须属于同一基本类型类别。

[规则 10.5] 表达式的值不宜强制转换为不合规的基本类型。

[规则 10.6] 复合表达式的值不得赋值给基本类型更宽的对象。

[规则 10.7] 若复合表达式作为执行通常算术转换的运算符的一个操作数,则另一个操作数不得使用更宽的基本类型。

[规则 10.8] 复合表达式的值不得强制转换为不同的基本类型类别,或更宽的基本类型。


指针类型转换

[规则 11.1] 不得在函数指针与任何其他类型之间进行转换。

[规则 11.2] 不得在指向不完整类型的指针与任何其他类型之间进行转换。

[规则 11.3] 不得在指向不同对象类型的指针之间进行转换。

[规则 11.4] 不宜在对象指针与整型之间进行转换。

[规则 11.5] 不宜将 void 指针转换为对象指针。

[规则 11.6] 不得在 void 指针与算术类型之间进行强制类型转换。

[规则 11.7] 不得在对象指针与非整型算术类型之间进行强制类型转换。

[规则 11.8] 类型转换不得移除指针所指向类型的任何 constvolatile_Atomic 限定。

[规则 11.9] 宏 NULL 是唯一允许使用的整型空指针常量形式。

[规则 11.10] _Atomic 限定符不得应用于不完整的 void 类型。


表达式

[规则 12.1] 表达式中运算符的优先级应显式明确。

[规则 12.2] 移位运算符的右操作数,取值范围必须在 0 到左操作数基本类型的比特宽度减 1 之间。

[规则 12.3] 不宜使用逗号运算符。

[规则 12.4] 常量表达式的计算不宜导致无符号整数回绕。

[规则 12.5] sizeof 运算符的操作数不得是声明为 “类型数组” 的函数参数。

[规则 12.6] 不得直接访问原子对象的结构体与联合体成员。


副作用

[规则 13.1] 初始化器列表中不得包含持续副作用。

[规则 13.2] 表达式的值及其持续副作用,在所有允许的求值顺序下必须保持一致,且不受线程交错执行的影响。

[规则 13.3] 包含自增 (++) 或自减 (--) 运算符的完整表达式,除自增或自减运算符本身带来的副作用外,不宜存在其他潜在副作用。

[规则 13.4] 不宜使用赋值运算符的运算结果。

[规则 13.5] 逻辑与 &&、逻辑或 || 运算符的右操作数,不得包含持续副作用。

[规则 13.6] sizeof 运算符的操作数中,不得包含任何存在潜在副作用的表达式。


控制语句表达式

[规则 14.1] 循环计数器不得使用本质为浮点的类型。

[规则 14.2] for 循环必须符合规范格式。

[规则 14.3] 控制表达式不得为不变量。

[规则 14.4] if 语句的控制表达式与迭代语句的控制表达式,必须使用本质为布尔的类型。


控制流

[规则 15.1] 不宜使用 goto 语句。

[规则 15.2] goto 语句必须跳转到同一函数中、声明在跳转语句之后的标签。

[规则 15.3] goto 语句引用的任何标签,必须声明在同一代码块中,或包裹该 goto 语句的任何外层代码块中。

[规则 15.4] 用于终止任意迭代语句的 breakgoto 语句,不宜超过一个。

[规则 15.5] 函数宜在末尾设置单一退出点。

[规则 15.6] 迭代语句或选择语句的主体必须是复合语句。

[规则 15.7] 所有 if ... else if 结构必须以 else 语句结尾。


switch 语句

[规则 16.1] 所有 switch 语句必须符合规范格式。

[规则 16.2] switch 标签只能在最内层闭合复合语句为 switch 语句体的场景下使用。

[规则 16.3] 每个 switch 子句都必须以无条件 break 语句终止。

[规则 16.4] 每个 switch 语句都必须包含 default 标签。

[规则 16.5] default 标签必须位于 switch 语句的第一个或最后一个 switch 标签位置。

[规则 16.6] 每个 switch 语句必须至少包含两个 switch 子句。

[规则 16.7] switch 表达式不得使用本质为布尔的类型。


函数

[规则 17.1] 不得使用标准头文件 <stdarg.h>

[规则 17.2] 函数不得直接或间接调用自身(禁止递归)。

[规则 17.3] 不得隐式声明函数。

[规则 17.4] 非 void 返回类型的函数,所有退出路径都必须带有带表达式的显式 return 语句。

[规则 17.5] 与声明为数组类型的形参对应的函数实参,必须包含合规数量的元素。

[规则 17.6] 数组参数的声明中,不得在[]之间使用 static 关键字。

[规则 17.7] 非 void 返回类型的函数的返回值必须被使用。

[规则 17.8] 不宜修改函数的形参。

[规则 17.9] 声明为 _Noreturn 的函数,不得返回到其调用方。

[规则 17.10] 声明为 _Noreturn 的函数,必须使用 void 返回类型。

[规则 17.11] 永不返回的函数,宜使用 _Noreturn 函数说明符声明。

[规则 17.12] 函数标识符只能配合前置 & 符号、或带括号的参数列表使用。

[规则 17.13] 不得对函数类型进行类型限定。


指针与数组

[规则 18.1] 指针算术运算生成的结果指针,必须与原指针操作数指向同一数组的元素。

[规则 18.2] 指针之间的减法运算,只能应用于指向同一数组元素的指针。

[规则 18.3] 关系运算符 >>=<<= 不得应用于指针类型表达式,除非它们指向同一对象的内部。

[规则 18.4] 不宜对指针类型表达式使用 +-+=-= 运算符。

[规则 18.5] 声明中的指针嵌套层级不宜超过两层。

[规则 18.6] 自动存储期或线程本地存储期对象的地址,不得复制到在该对象销毁后仍持续存在的其他对象中。

[规则 18.7] 不得声明柔性数组成员。

[规则 18.8] 不得使用变长数组。

[规则 18.9] 临时生存期的对象不得进行数组到指针的转换。

[规则 18.10] 不得使用指向可变修改数组类型的指针。


重叠存储

[规则 19.1] 不得向重叠的对象进行赋值或拷贝操作。

[规则 19.2] 不宜使用 union 联合体关键字。


预处理指令

[规则 20.1] #include 指令之前只能出现预处理指令或注释。

[规则 20.2] 头文件名中不得出现 '"\ 字符,以及 /*// 字符序列。

[规则 20.3] #include 指令之后必须跟随 <filename>"filename" 格式的内容。

[规则 20.4] 不得定义与关键字重名的宏。

[规则 20.5] 不宜使用 #undef 指令。

[规则 20.6] 宏参数中不得出现形似预处理指令的标记。

[规则 20.7] 宏参数展开后生成的表达式,必须包裹在圆括号中。

[规则 20.8] #if#elif 预处理指令的控制表达式,求值结果必须为 01

[规则 20.9] #if#elif 预处理指令的控制表达式中使用的所有标识符,必须在求值前通过 #define 定义。

[规则 20.10] 不宜使用 ### 预处理运算符。

[规则 20.11] 紧跟 # 运算符的宏参数,不得再紧跟 ## 运算符。

[规则 20.12] 作为 ### 运算符操作数的宏参数,若其本身需要进一步宏替换,则只能作为这两个运算符的操作数使用。

[规则 20.13] 首个标记为 # 的行,必须是合法的预处理指令。

[规则 20.14] 所有 #else#elif#endif 预处理指令,必须与其对应的 #if#ifdef#ifndef 指令位于同一文件中。


标准库

[规则 21.1] 不得对保留标识符或保留宏名使用 #define#undef 指令。

[规则 21.2] 不得声明保留标识符或保留宏名。

[规则 21.3] 不得使用 <stdlib.h> 中的内存分配与释放函数。

[规则 21.4] 不得使用标准头文件 <setjmp.h>

[规则 21.5] 不得使用标准头文件 <signal.h>

[规则 21.6] 不得使用标准库的输入输出函数。

[规则 21.7] 不得使用 <stdlib.h> 中的 atofatoiatolatoll 函数。

[规则 21.8] 不得使用 <stdlib.h> 中的标准库程序终止函数。

[规则 21.9] 不得使用 <stdlib.h> 中的 bsearchqsort 标准库函数。

[规则 21.10] 不得使用标准库的时间与日期函数。

[规则 21.11] 不宜使用标准头文件 <tgmath.h>

[规则 21.12] 不得使用标准头文件 <fenv.h>

[规则 21.13] 传递给 <ctype.h> 中函数的任何值,必须能表示为 unsigned char 类型,或为 EOF 值。

[规则 21.14] 不得使用 memcmp 函数比较以空字符结尾的字符串。

[规则 21.15] 标准库函数 memcpymemmovememcmp 的指针实参,必须是指向兼容类型的限定或非限定版本的指针。

[规则 21.16] 标准库函数 memcmp 的指针实参,必须指向指针类型、本质有符号类型、本质无符号类型、本质布尔类型或本质枚举类型。

[规则 21.17] 使用 <string.h> 中的字符串处理函数时,不得导致对其指针参数所引用对象的越界访问。

[规则 21.18] 传递给 <string.h> 中任意函数的 size_t 类型参数,必须为合规值。

[规则 21.19] 标准库函数 localeconvgetenvsetlocalestrerror 返回的指针,只能作为指向 const 限定类型的指针使用。

[规则 21.20] 标准库函数 asctimectimegmtimelocaltimelocaleconvgetenvsetlocalestrerror 返回的指针,在后续调用同一函数后不得再使用。

[规则 21.21] 不得使用 <stdlib.h> 中的标准库 system 函数。

[规则 21.22] <tgmath.h> 中声明的任意泛型宏的所有操作数实参,必须使用合规的基本类型。

[规则 21.23] <tgmath.h> 中声明的任意多参数泛型宏的所有操作数实参,必须使用相同的标准类型。

[规则 21.24] 不得使用 <stdlib.h> 中的随机数生成函数。

[规则 21.25] 所有内存同步操作必须以顺序一致性序执行。

[规则 21.26] 标准库函数 mtx_timedlock() 只能在具备合规互斥锁类型的互斥锁对象上调用。


资源

[规则 22.1] 通过标准库函数动态获取的所有资源,必须被显式释放。

[规则 22.2] 仅当内存块通过标准库函数分配时,才能对其进行释放操作。

[规则 22.3] 同一文件不得在不同流上同时以读写模式打开。

[规则 22.4] 不得尝试向以只读模式打开的流执行写入操作。

[规则 22.5] 不得对 FILE 对象指针进行解引用。

[规则 22.6] 关联的流关闭后,不得再使用指向该 FILE 对象的指针。

[规则 22.7] 宏 EOF 只能与可返回 EOF 的标准库函数的未修改返回值进行比较。

[规则 22.8] 调用会设置 errno 的函数之前,必须将 errno 的值置为 0

[规则 22.9] 调用会设置 errno 的函数之后,必须检查 errno 的值是否为 0

[规则 22.10] 仅当最近一次调用的函数是会设置 errno 的函数时,才能对 errno 的值进行检查。

[规则 22.11] 此前已执行过加入或分离操作的线程,不得再次执行加入或分离操作。

[规则 22.12] 线程对象、线程同步对象和线程本地存储指针,只能通过对应的标准库函数访问。

[规则 22.13] 线程对象、线程同步对象和线程本地存储指针,必须具备合规的存储期。

[规则 22.14] 线程同步对象在被访问前,必须完成初始化。

[规则 22.15] 线程同步对象和线程本地存储指针,必须在所有访问它们的线程终止后,才能被销毁。

[规则 22.16] 线程锁定的所有互斥锁对象,必须由同一线程显式解锁。

[规则 22.17] 线程不得解锁未被自身锁定的互斥锁,也不得对未被自身锁定的互斥锁调用 cnd_wait()cnd_timedwait() 函数。

[规则 22.18] 不得对非递归互斥锁执行递归锁定操作。

[规则 22.19] 一个条件变量最多只能关联一个互斥锁对象。

[规则 22.20] 线程本地存储指针在被访问前,必须完成创建。


泛型选择

[规则 23.1] 泛型选择宜仅通过宏展开使用。

[规则 23.2] 非宏展开的泛型选择,其控制表达式中不得包含潜在副作用。

[规则 23.3] 泛型选择宜至少包含一个非默认关联。

[规则 23.4] 泛型关联必须列出合规的类型。

[规则 23.5] 泛型选择不宜依赖隐式指针类型转换。

[规则 23.6] 泛型选择的控制表达式,其基本类型必须与标准类型匹配。

[规则 23.7] 宏展开的泛型选择,应对其参数仅求值一次。

[规则 23.8] 泛型选择的默认关联,必须位于第一个或最后一个关联位置。