一、C语言的缺陷
1、语言误用的可能
(1)因为C语言对语法要求不严格,这就导致编译器发现错误变得困难,而且如果没有规范约束,程序员还可能会写出晦涩难懂的代码。
(2)举例来说,比如需要写一个条件语句判断a与b是否相等,如果不加以约束,可能会出现以下情况,无论是正确的写法还是错误的写法,都是能够通过编译的。
cpp
if( a == b ); //正确写法,判断a与b是否相等
if( a = b); //错误写法,将b的值赋给a,判断a是否不为0
2、弱类型语言带来的缺陷
(1)C语言是弱类型语言,它允许两个不同数据类型的变量之间进行相互运算,虽然大多数情况下都能得到预期的运算结果,但也难免会损耗性能。
(2)在做两个不同数据类型的变量之间的相互运算时,最稳妥的方法是对其中一个数据类型的变量进行强制类型转换,确保转换后参与运算的数据都是同一数据类型的。
cpp
int a = 5;
float b = 5.1;
int c;
c = a + (int)b; //因为期望得到的结果c是int类型的,所以将b强制转换为int类型
3、缺乏运行时的错误检查
(1)C语言编译器一般不为常见问题提供运行时检查,比如数据溢出、指针的有效性、零除问题或数组越界,这些错误一般都是在程序正式运行以后才被发现。
(2)如下所示的主函数就存在越界访问的情况,一般C语言编译器是不会报错的。
cpp
int main(){
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i <= 10; i++)
{
printf("%d\n", arr[i]); //当i等于10的时候,越界访问了
}
return 0;
}
二、MISRA C的发展史
大部分车载软件都是使用C语言开发的,其执行效率高,代码量小,被静态分析和测试工具广泛地支持,尽管它存在缺陷,而为了减小这些缺陷引起功能安全问题,汽车工业软件可靠性协会(Motor IndustrySoftware Reliability Association,MISRA)在1998年发布了第一版针对汽车工业软件安全性的C语言编码规范------MISRA C
截止至2025年,MISRA C经过了如下的版本迭代,不过当前使用率最高的版本还是MISRA C 2012

三、MISRA C 2012的编码规范介绍
1、规则分类
(1)Directive类:仅分析源码无法对指令进行合规性判定,往往需要结合设计文档或开发人员经验综合判定,静态测试工具可以为指令的合规性判定提供辅助,但不同性能的工只提供的解释可能会大不相同。
(2)Rule类:仅根据规则即可以对源码进行合规性检查,不需要额外的设计文档和需求规范。
2、规则类别
(1)Mandatory(强制)类:声称符合MISRA C 2012的C代码应遵守所有的强制性准则,不允许背离强制性规则。
(2)Required(必须)类:声称符合MISRAC2012的C代码应该遵循所有的要求规则,如果存在违规行为,需要给出形式化的背离。
(3)Advisory(建议)类:形式化的背离对于"建议规则"不是必需的,但是,如果不遵循形式化的背离过程,应为违规行为的记录做出其它安排。
3、规则的可判定性
(1)可判定性仅适用于Rule类规则,可判定性的区分标准是------是否能在任何情况下明确回答"该代码是否遵循了这条规则"。
(2)可判定的规则:
①可判定的规则通过直接检查代码文本能够直接判断是否违规。
②静态分析器报告了Decidable(可判定)规则的诊断消息,说明代码一定违反了该条规则;静态分析器未报告Decidable(可判定)规则的诊断消息,说明代码一定未违反该条规则。
③如下几条规则都是可判定的规则:
Rule 5.2:取决于名称和标识符的范围
Rule 11.3:取决于原始指针和目标指针的类型
Rule 20.7:取决于宏扩展结果的句法形式
(3)不可判定的规则:
①不可判定的规则无法通过直接检查代码文本能够直接判断是否违规。
②静态分析器报告了Undecidable(不可判定)规则的诊断消息,代码不一定违反了该条规则;静态分析器未报告Undecidable(不可判定)规则的诊断消息,代码不一定未违反该条规则。
③如下几条规则都是不可判定的规则:
Rule 2.1:取决于知道执行是否从未达到某个特定点
Rule 12.2:取决于移位运算符的右侧操作数的值(这个值如果是变量,那么在程序运行前大概率是不确定的)
4、Rule规则的分析范围
(1)通过独立地检查每一个编译单元(Translation Unit),有可能发现工程中的所有违规情况,则该规则的分析范围为单个编译单元(Single Translation Unit)。
Rule 16.4:每个switch语句都必须包含一个default标签
switch语句只会在单个文件中定义,不跨文件定义,所以其分析范围为单个编译单元
(2)要确认在编译单元(Translation Unit)中违反某条规矩,仅检测这个存在问题的编译单元是不够的,则该规则的分析范围为系统(System)。
Rule 9.1:变量在使用前必须经过赋值
变量的使用和定义可能不仅存在于同一个文件中,所以其分析范围为系统
5、几条Directive类规则举例
(1)Dir 4.6:应使用指示大小和有符号性的类型定义来替代基本数值类型,不应使用char、short、int、long、long long(C99)、float、double和long、double这些基本数据类型,因为在不同的编译器中,一个数据类型的长度可能是不一致的,所以需要用typedef关键字在别称中指定数据类型的长度。
cpp
int i = 0; //不合规
typedef int SINT_16; //合规,别称中指定了长度
typedef int speed_t; //不合规,别称中没有指定符号和大小
typedef int16_t torque_t; //合规,进一步的抽象不需要特定的长度
(2)Dir 4.10:应该采取预防措施防止头文件被多次包含,如果一个头文件被工程中的源码包含了多次,可能会导致定义的冲突进而引发未定义或错误行为,对此可规范头文件编写格式,用条件编译来实现防止重复编译。
cpp
#ifndef __TEST_H__
#define __TEST_H__
//头文件的内容
#endif //__TEST_H__
(3)Dir 4.12:不应使用动态内存分配,任何C的标准库函数的动态内存分配和进程的释放都可能导致未定义的行为。
cpp
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *p = (char*)malloc(10);
char *q;
free(p);
q = p; //未定义的行为,p值不确定,p指针指向的空间释放后应该将其置为NULL
return 0;
}
6、几条Rule类规则举例
(1)Rule 12.1:应用小括号明确表达式中各运算符的优先级。
cpp
a[i]->n; //合规,无需写成(a[i])->n
*p++; //合规,无需写成*(p++),但建议写成*(p++)
sizeof x + y; //不合规,应写成sizeof(x) + y或sizeof(x + y)明确优先级
(2)Rule 13.3:包含递增或递减运算的表达式,除了由递增或者递减运算符引起的副作用之外(副作用可认为是操作数的值发生变化),运算符不应该包含其它潜在的副作用,也就是说,不建议将增量和减量运算符与其它运算符组合使用,因为它显著地损害代码的可读性,还可能引入额外的副作用导致产生未定义的行为。
cpp
a = a++; //合规,但没必要,可写成a++
a = ++b + c--; //不合规,可能会产生非预期的行为
四、静态分析工具
要验证开发人员编写的C代码是否合规,如果仅靠人工检查代码,将会耗费大量时间,且审查的结果并不完全可靠,对此可使用自动化工具执行编码规范,将编写好的代码交由静态分析工具进行静态分析
静态分析可快速准确地检查出程序中的各种错误(如同编译器指出代码中的错误和警告一样,不过静态分析工具会额外检查是否满足MISRA C的规则),使得代码审查和测试阶段变的更加有效,在软件开发初期及时发现不利于软件移植、维护等缺陷,降低软件开发成本
Perforce公司的静态分析工具Helix QAc是汽车行业主流的代码静态分析工具,具有业界领先的编码规范覆盖度,对MISRAC 2012与MISRAC 2003的覆盖度高达100%
五、MISRA C 2012规范
1、Directive类
|----------|----------------------------------------|----|----------|
| 规则ID | 说明 | 类别 | 静态分析工具支持 |
| Dir 1.1 | 应该记录并了解程序输出依赖的任何实现定义行为 | 必须 | 否 |
| Dir 2.1 | 所有源文件应该在没有任何编译错误的情况下编译 | 必须 | 否 |
| Dir 3.1 | 所有代码都必须能按照记录的要求进行跟踪 | 必须 | 否 |
| Dir 4.1 | 应该最大限度减少运行时失败 | 必须 | 否 |
| Dir 4.2 | 应该记录所有使用汇编语言的情况 | 建议 | 否 |
| Dir 4.3 | 应该独立封装汇编语言 | 必须 | 是 |
| Dir 4.4 | 不应将代码段"注释掉" | 建议 | 是 |
| Dir 4.5 | 同一命名空间中发生重叠的标识符在排字上应该清楚明确 | 建议 | 是 |
| Dir 4.6 | 应该使用指示大小和符号的typedef代替基本数值类型 | 建议 | 是 |
| Dir 4.7 | 如果函数返回了错误信息,则应该测试该错误信息 | 必须 | 是 |
| Dir 4.8 | 如果结构体或union的指针在编译单元内从未被解引用,则应该隐藏该对象的实现 | 建议 | 是 |
| Dir 4.9 | 如果函数和类似于函数的宏可互换,则应优先使用函数 | 建议 | 是 |
| Dir 4.10 | 应注意防止头文件的内容出现多次 | 必须 | 是 |
| Dir 4.11 | 应该检查传递给库函数的值的有效性 | 必须 | 是 |
| Dir 4.12 | 不应使用动态内存分配 | 必须 | 是 |
| Dir 4.13 | 应该按正确的顺序调用专门针对资源操作的函数 | 建议 | 是 |
| Dir 4.14 | 应该检查从外部源接收的值的有效性 | 必须 | 是 |
2、Rule类
|------------|--------------------------------------------------------------------------------------------|----|----------|
| 规则ID | 说明 | 类别 | 静态分析工具支持 |
| Rule 1.1 | 程序不应包含任何违反标准C语法和约束的情况,并且不应超出实现的转换限制 | 必须 | 是 |
| Rule 1.2 | 不应使用语言扩展 | 建议 | 是 |
| Rule 1.3 | 不应出现不确定行为或关键未指定行为 | 必须 | 否 |
| Rule 1.4 | 不应使用编程语言最新版本中引入的、尚未被广泛采用或尚未完全标准化的语法、关键字、库函数或编程模式 | 必须 | 是 |
| Rule 2.1 | 项目不应包含无法到达的代码(即永远不会运行的代码) | 必须 | 是 |
| Rule 2.2 | 不应存在无用代码 | 必须 | 是 |
| Rule 2.3 | 项目不应包含未使用的类型声明 | 建议 | 是 |
| Rule 2.4 | 项目不应包含未使用的标记声明 | 建议 | 是 |
| Rule 2.5 | 项目不应包含未使用的宏声明 | 建议 | 是 |
| Rule 2.6 | 函数不应包含未使用的标签声明 | 建议 | 是 |
| Rule 2.7 | 函数中不应存在未使用的参数 | 建议 | 是 |
| Rule 3.1 | 不应在注释中使用字符序列"/*"和"//" | 必须 | 是 |
| Rule 3.2 | 不应在"//"注释中使用行合并 | 必须 | 是 |
| Rule 4.1 | 应该终止八进制和十六进制转义序列 | 必须 | 是 |
| Rule 4.2 | 不应使用三字符组 | 建议 | 是 |
| Rule 5.1 | 外部标识符应该是不同的 | 必须 | 是 |
| Rule 5.2 | 在同一范围和命名空间中声明的标识符应该是不同的 | 必须 | 是 |
| Rule 5.3 | 在内部范围中声明的标识符不应隐藏在外部范围中声明的标识符 | 必须 | 是 |
| Rule 5.4 | 宏标识符应该是不同的 | 必须 | 是 |
| Rule 5.5 | 标识符应该不同于宏名称 | 必须 | 是 |
| Rule 5.6 | typedef名称应是唯一的标识符 | 必须 | 是 |
| Rule 5.7 | 标记名称应是唯一的标识符 | 必须 | 是 |
| Rule 5.8 | 使用外部链接定义对象或函数的标识符应该唯一 | 必须 | 是 |
| Rule 5.9 | 使用内部链接定义对象或函数的标识符应该唯一 | 建议 | 是 |
| Rule 6.1 | 位域只应通过适当的类型声明 | 必须 | 是 |
| Rule 6.2 | 单个位已命名位域不应是signed类型 | 必须 | 是 |
| Rule 7.1 | 不应使用八进制常量 | 必须 | 是 |
| Rule 7.2 | 应对unsigned类型中表示的所有整数常量应用"u"或"U"后缀 | 必须 | 是 |
| Rule 7.3 | 不应在常数值后缀中使用小写字符"l" | 必须 | 是 |
| Rule 7.4 | 不应为对象分配字符串常量,除非该对象的类型为"const限定char类型指针" | 必须 | 是 |
| Rule 8.1 | 应显式指定数据类型 | 必须 | 是 |
| Rule 8.2 | 写函数时要明确写出参数类型和参数名,不能省略 | 必须 | 是 |
| Rule 8.3 | 对象或函数的所有声明都应使用相同的名称和类型限定符 | 必须 | 是 |
| Rule 8.4 | 当使用外部链接定义对象或函数时,兼容声明应该可见 | 必须 | 是 |
| Rule 8.5 | 外部对象或函数应在一个且只应在一个文件中声明一次 | 必须 | 是 |
| Rule 8.6 | 包含外部链接的标识符应只具有一个外部定义 | 必须 | 是 |
| Rule 8.7 | 如果函数或对象只在一个编译单元中引用,则不应该使用外部链接定义该函数或对象 | 建议 | 是 |
| Rule 8.8 | 应在包含内部链接的对象和函数的所有声明中使用static存储class说明符 | 必须 | 是 |
| Rule 8.9 | 如果对象的标识符只出现在一个函数中,则应该在块范围内定义该对象 | 建议 | 是 |
| Rule 8.10 | 内联函数应该通过static存储class声明 | 必须 | 是 |
| Rule 8.11 | 在声明具有外部链接的数组时,应显式指定其大小 | 建议 | 是 |
| Rule 8.12 | 在枚举器列表中,隐式指定的枚举常量的值应该唯一 | 必须 | 是 |
| Rule 8.13 | 指针应尽量指向const限定类型 | 建议 | 是 |
| Rule 8.14 | 不应使用限制类型限定符 | 必须 | 是 |
| Rule 9.1 | 不应在设置具有自动存储期的对象的值之前读取该值,即变量在使用前必须经过赋值 | 强制 | 是 |
| Rule 9.2 | 聚合或union的初始化器应使用大括号括起 | 必须 | 是 |
| Rule 9.3 | 不应将数组部分初始化 | 必须 | 是 |
| Rule 9.4 | 对象的元素不应多次初始化 | 必须 | 是 |
| Rule 9.5 | 使用指定的初始化器对数组对象执行初始化时,应显式指定数组的大小 | 必须 | 是 |
| Rule 10.1 | 操作数的类型不应是不恰当的基本类型 | 必须 | 是 |
| Rule 10.2 | 不应在加法和减法运算中通过不当的方式使用基本字符类型的表达式 | 必须 | 是 |
| Rule 10.3 | 不应将表达式的值赋值给为较窄的基本类型或不同基本类型类别的对象 | 必须 | 是 |
| Rule 10.4 | 对运算符的两个操作数执行常用算术转换应该具有相同的基本类型类别 | 必须 | 是 |
| Rule 10.5 | 不应将表达式的值转换为不适当的基本类型 | 建议 | 是 |
| Rule 10.6 | 不应将复合表达式的值赋值给具有较宽基本类型的对象 | 必须 | 是 |
| Rule 10.7 | 在常用算术转换中如果将复合表达式用作运算符的一个操作数,则另一个操作数不应具有较宽的基础类型 | 必须 | 是 |
| Rule 10.8 | 不应将复合表达式的值转换为不同的基本类型类别或较宽的基本类型 | 必须 | 是 |
| Rule 11.1 | 指向函数的指针不应转换为任何其它类型 | 必须 | 是 |
| Rule 11.2 | 指向不完整类型的指针不应转换为任何其它类型 | 必须 | 是 |
| Rule 11.3 | 指向对象类型的指针不应转换为指向不同对象类型的指针 | 必须 | 是 |
| Rule 11.4 | 指向对象的指针不应转换为整数类型 | 建议 | 是 |
| Rule 11.5 | 指向void的指针不应转换为指向对象的指针 | 建议 | 是 |
| Rule 11.6 | 指向void的指针不应转换为算术运算类型 | 必须 | 是 |
| Rule 11.7 | 指向对象的指针不应转换为非整数算术运算类型 | 必须 | 是 |
| Rule 11.8 | 指针所指向类型的转换不应移除任何const或volatile属性 | 必须 | 是 |
| Rule 11.9 | 宏NULL应该是唯一允许的整数null指针常量形式 | 必须 | 是 |
| Rule 12.1 | 不应依赖C/C++语言内置的运算符优先级规则,而应该用括号显式地表达运算的先后顺序 | 建议 | 是 |
| Rule 12.2 | 移位operator的右操作数应介于零和左操作数基本类型的位宽度之间 | 必须 | 是 |
| Rule 12.3 | 不应使用逗号运算符 | 建议 | 是 |
| Rule 12.4 | 常量表达式的求值不应导致unsigned的整数溢出 | 建议 | 是 |
| Rule 12.5 | sizeof 运算符的操作数不应是声明为"类型数组"的函数参数 | 强制 | 是 |
| Rule 13.1 | 初始化器列表不应包含持久的其它作用,比如修改其它变量 | 必须 | 是 |
| Rule 13.2 | 在所有允许的评估顺序中表达式的值及其持久的其它作用应该保持相同 | 必须 | 是 |
| Rule 13.3 | 包含递增或递减运算符的完整表达式不应包含除了递增或递减运算符导致的影响之外的其它潜在作用 | 建议 | 是 |
| Rule 13.4 | 不应使用赋值运算符的结果 | 建议 | 是 |
| Rule 13.5 | 逻辑运算符&&或||的右操作数不应包含持久的其它作用,比如修改其它变量 | 必须 | 是 |
| Rule 13.6 | sizeof运算符的操作数不应包含具有潜在其它作用的任何表达式,比如修改其它变量 | 强制 | 是 |
| Rule 14.1 | 循环计数器不应具有基本的浮点类型 | 必须 | 是 |
| Rule 14.2 | for循环应该符合语法 | 必须 | 是 |
| Rule 14.3 | 控制表达式不应是不变量,比如if语句的判断的条件不能是常数 | 必须 | 是 |
| Rule 14.4 | if语句的控制表达式和迭代语句的控制表达式应该具有基本的布尔类型 | 必须 | 是 |
| Rule 15.1 | 不应使用goto语句 | 建议 | 是 |
| Rule 15.2 | goto语句应跳转到在同一函数后半部分中声明的标签 | 必须 | 是 |
| Rule 15.3 | goto语句引用的任何标签都应在同一代码块或包括该goto语句的代码块中声明 | 必须 | 是 |
| Rule 15.4 | 用于结束任何迭代语句的break或goto语句不应超过一个 | 建议 | 是 |
| Rule 15.5 | 函数在结束处应该只有唯一的退出点 | 建议 | 是 |
| Rule 15.6 | 迭代语句或选择语句的主体应该是复合语句 | 必须 | 是 |
| Rule 15.7 | 所有if...elseif结构应以else语句结束 | 必须 | 是 |
| Rule 16.1 | 所有switch语句都应该符合语法 | 必须 | 是 |
| Rule 16.2 | switch标签只应在最里层的复合语句是switch语句的主体时使用 | 必须 | 是 |
| Rule 16.3 | 无条件的break语句应该终止每一个switch子句 | 必须 | 是 |
| Rule 16.4 | 每个switch语句都应具有default标签 | 必须 | 是 |
| Rule 16.5 | default标签应显示为 switch语句的第一个或最后一个switch标签 | 必须 | 是 |
| Rule 16.6 | 每个switch语句都应该至少具有两个switch子句 | 必须 | 是 |
| Rule 16.7 | switch表达式不应具有基本的布尔类型 | 必须 | 是 |
| Rule 17.1 | 不应使用<stdarg.h>的功能 | 必须 | 是 |
| Rule 17.2 | 函数不应直接或间接调用自身 | 必须 | 是 |
| Rule 17.3 | 不应隐式声明函数 | 强制 | 是 |
| Rule 17.4 | 返回非void类型的函数的所有退出路径都应具有包含表达式的显式return语句 | 强制 | 是 |
| Rule 17.5 | 被声明为具有数组类型的参数对应的函数参数应该具有适当数量的元素 | 建议 | 是 |
| Rule 17.6 | 数组参数的声明不应在[]之间包含static关键字 | 强制 | 是 |
| Rule 17.7 | 应使用由返回非void类型的函数返回的值 | 必须 | 是 |
| Rule 17.8 | 函数体内不应对函数参数做修改 | 建议 | 是 |
| Rule 18.1 | 对指针进行算术运算(如加减整数)时,运算结果指针必须与原始指针指向同一个数组对象(或同一块连续内存区域)中的元素,或者指向该数组最后一个元素的下一个位置(尾后位置) | 必须 | 是 |
| Rule 18.2 | 指针之间的减法运算只应该应用到访问同一数组的元素的指针 | 必须 | 是 |
| Rule 18.3 | 只有当比较的两个指针指向同一个数组(或同一块连续内存区域)中的元素时,才能使用关系运算符 | 必须 | 是 |
| Rule 18.4 | 不应对指针类型的表达式应用+、-、+=和-=运算符 | 建议 | 是 |
| Rule 18.5 | 声明不应包含超过两级的指针嵌套 | 建议 | 是 |
| Rule 18.6 | 在第一个对象消失后不应将自动存储对象的地址拷贝给另一个可能仍然存在的对象 | 必须 | 是 |
| Rule 18.7 | 不应声明可变的数组成员 | 必须 | 是 |
| Rule 18.8 | 不应使用可变长度数组类型 | 必须 | 是 |
| Rule 19.1 | 不应将对象分配或拷贝给重叠的对象 | 建议 | 是 |
| Rule 19.2 | 不应使用union键字 | 强制 | 是 |
| Rule 20.1 | #include指令之前只能包含其它预处理器指令或注释 | 建议 | 是 |
| Rule 20.2 | 逗号或双引号字符以及/*或//字符序列不应出现在头文件名称中 | 必须 | 是 |
| Rule 20.3 | #include 指令应后接<filename>或"filename"序列 | 必须 | 是 |
| Rule 20.4 | 不应将关键字定义为宏的名称 | 必须 | 是 |
| Rule 20.5 | 不允许使用#undef | 建议 | 是 |
| Rule 20.6 | 类似于预处理指令的标识符不应出现在宏参数中 | 必须 | 是 |
| Rule 20.7 | 通过扩展宏参数得到的表达式应使用圆括号括起 | 必须 | 是 |
| Rule 20.8 | #if或#elif预处理指令的控制表达式的值应评估为0或1 | 建议 | 是 |
| Rule 20.9 | #if或#elif预处理指令的控制表达式中使用的所有标识符应该在之前使用#define定义 | 必须 | 是 |
| Rule 20.10 | 不应使用#和##预处理器运算符 | 建议 | 是 |
| Rule 20.11 | 后面紧接#运算符的宏参数后面不应紧接##运算符 | 必须 | 是 |
| Rule 20.12 | 用作#或##运算符的操作数的宏参数(本身要接受进一步的宏替换)只应用作这些运算符的操作数 | 必须 | 是 |
| Rule 20.13 | 第一个标识符为#的行应该是有效的预处理指令 | 必须 | 是 |
| Rule 20.14 | 所有#else、#elif和#endif预处理器指令都应和相关的#if、#ifdef或#ifdef指令处在同一文件中 | 必须 | 是 |
| Rule 21.1 | 不应将保留的标识符或保留的宏名称与#define或#undef一起使用 | 必须 | 是 |
| Rule 21.2 | 不应声明保留的标识符或宏名称 | 必须 | 是 |
| Rule 21.3 | 不应使用<stdlib.h>的内存分配和释放函数 | 必须 | 是 |
| Rule 21.4 | 不应使用标准头文件<setjmp.h> | 必须 | 是 |
| Rule 21.5 | 不应使用标准头文件<signal.h> | 必须 | 是 |
| Rule 21.6 | 不应使用标准库输入/输出函数 | 必须 | 是 |
| Rule 21.7 | 不应使用<stdlib.h>的atof、atoi、atol和atoll 函数 | 必须 | 是 |
| Rule 21.8 | 不应使用<stdlib.h>的库函数abort、exit和system | 必须 | 是 |
| Rule 21.9 | 不应使用<stdlib.h>的库函数bsearch和qsort | 必须 | 是 |
| Rule 21.10 | 不应使用标准库时间和日期函数 | 必须 | 是 |
| Rule 21.11 | 不应使用标准头文件<tgmath.h> | 必须 | 是 |
| Rule 21.12 | 不应使用<fenv.h>的异常处理功能 | 建议 | 是 |
| Rule 21.13 | 在<ctype.h>中传递给函数的任何值都应表示为unsigned char或者值EOF | 强制 | 是 |
| Rule 21.14 | 不应使用标准库函数memcmp与以null终止的字符串进行比较 | 必须 | 是 |
| Rule 21.15 | 标准库函数memcpy、memmove和memcmp的指针参数应该是指向兼容类型的合格或不合格版本的指针 | 必须 | 是 |
| Rule 21.16 | 标准库函数memcmp的指针参数应该只指向signed类型、unsigned类型、boolean类型或者enum类型 | 必须 | 是 |
| Rule 21.17 | 使用来自<string.h>中的字符串处理函数不应导致访问超出它们的指针参数引用的对象的范围 | 强制 | 是 |
| Rule 21.18 | 传递给<string.h>中的任何函数的size_t参数都应有一个适当的值 | 强制 | 是 |
| Rule 21.19 | 只应将标准库函数localeconv、getenv、setlocale或strerror返回的指针当作指向const限定类型的数据的指针来使用 | 强制 | 是 |
| Rule 21.20 | 标准函数asctime、ctime、gmtime、localtime、localeconvgetenv、setlocale或strerror返回的指针不应后接调用相同函数的后续调用 | 强制 | 是 |
| Rule 21.21 | 不应使用<stdlib.h>的标准库函数system | 必须 | 是 |
| Rule 22.1 | 应显式释放通过标准库函数动态获取的所有资源 | 必须 | 是 |
| Rule 22.2 | 内存块只应在其通过标准库函数分配的情况下释放 | 强制 | 是 |
| Rule 22.3 | 同一文件在不同的数据流中不应同时以读取和写入权限打开 | 必须 | 是 |
| Rule 22.4 | 不应尝试写入以只读方式打开的数据流 | 强制 | 是 |
| Rule 22.5 | 不应解引用FILE对象的指针 | 强制 | 是 |
| Rule 22.6 | FILE指针的值不应在关联数据流已关闭后使用 | 强制 | 是 |
| Rule 22.7 | 宏EOF只应与能够返回EOF的任何标准库函数的未修改返回值进行比较 | 必须 | 是 |
| Rule 22.8 | 在调用 errno-setting-function之前,应将errno的值设置为零 | 必须 | 是 |
| Rule 22.9 | 在调用 errno-setting-function之后,应测试 errno的值是否为零 | 必须 | 是 |
| Rule 22.10 | 仅当要调用的最后一个函数为errno-setting-function时,才应测试errno的值 | 必须 | 是 |