【(C++Test 代码静态分析问题修改(MISRA标准))】
发布时间
阅读量:
阅读量
@[TOC](C++Test 代码静态分析问题修改(MISRA标准))
文章目录
- 问题类型
- 问题类型案例分析与解决
- 未完待续
问题类型
总表:有需要按序号进行搜索
- 函数在定义和调用之时必须始终有可见的原型 (MISRA-071-3)
- 外部对象不得声明在多个文件中 (MISRA-027-3)
- for 循环语句的三个表达式应该仅与循环控制相关 (MISRA2004-13_5-3)
- switch 语句中最后必须为 default 子句 (MISRA2004-15_3-3)
- 不允许其结果是不变的布尔运算 (MISRA2004-13_7_t-3)
- 不能将typedef的名字重用在另一个typedef名字 (MISRA2004-5_3_b-3)
- 不能重用typedf名字 (MISRA2004-5_3_a-3)
- 不要使用可变数量的参数来定义函数 (MISRA2004-16_1-3)
- 函数不应该直接或者间接地调用自己 (MISRA2004-16_2-3)
- 函数的声明与原型的类型必须一致 (MISRA2004-8_3_b-3)
- 在内部范围的标识符不能和外部的标识符用同样的名字,因为会隐藏那个标识符 (MISRA2004-5_2_b-3)
- 在循环体中至多只允许一个用于结束循环的brak语句(MISRA2004-14_6-3)
- 在数组和结构提的非0初始化中,使用大括号进行标识和匹配 (MISRA2004-9_2-3)
- 如果函数返回错误信息,该错误信息必须经过测试 (MISRA2004-16_10-3)
- 如果对无符号字符型或无符号短整形进行~和<<位运算后,其结果应立即强制转换成操作数的基本类型 (MISRA2004-10_5-3)
- 带有自动存储空间的对象地址不能被分配给另一个对象,该对象销毁后,另一个对象仍然存在,但指针成为野指针 (MISRA2004-17_6-3)
- 必须以break作为switch每个子句的结束语句 (MISRA2004-15_2-3)
- 所有的"if…else-if"结构中应该由"else"分支结束 (MISRA2004-14_10-3)
问题类型案例分析与解决
问题类型 1
| 问 题 序号1 | 函数在定义和调用之时必须始终有可见的原型(MISRA-071-3) |
|---|---|
| 提 示 | [行 116] 函数的原型 ’Main_Ctrl_Data_Temp_Match_Data‘没有先于函数的定义 |
| 分 析 | 由于函数未声明或声明在函数之后引起 |
| 解 决 | 1.先在项目工程中搜索该函数,确定没有在其他位置进行声明;2.在函数引用之前对函数进行声明;3.如果已在其他位置进行声明,对该头文件进行引用 |
如图,我的工程中是有定义的,仔细看Ctrl_Temp_Match_Data.c引用的头文件没有包含kcg_imported_functions.h文件,所以进行添加。
问题类型 2
| 问 题 序号2 | 函数在定义和调用之时必须始终有可见的原型(MISRA-071-3) |
|---|---|
| 提 示 | [行 116] 函数的原型 ’Add_Serve_Voice‘没有先于函数的定义 |
| 分 析 | 由于函数未声明或声明在函数之后引起 |
| 解 决 | 1.先在项目工程中搜索该函数,确定没有在其他位置进行声明;2.在函数引用之前对函数进行声明;3.如果已在其他位置进行声明,对该头文件进行引用 |
问题类型 3
| 问 题 序号3 | for 循环语句的三个表达式应该仅与循环控制相关 (MISRA2004-13_5-3) |
|---|---|
| 提 示 | 通过在 for 循环语句的第一个表达式中单独的赋值来初始化循化计数器 |
| 分 析 | for 循环语句的第一个表达式只能进行赋值操作,不应有额外的计算 |
| 解 决 | 找到问题所在如下图。因为要用到之前的i的值,所以这里是这么写的,我们应该在循环开始之前把i的值赋给中间变量,在for循环的初始化时候,把中间变量赋给i |
这里改成
temp = i - 1;
for ( i = temp ; i > 0L ; i- -)
问题类型 3
| 问 题 序号3 | 函数在定义和调用之时必须始终有可见的原型(MISRA-071-3) |
|---|---|
| 提 示 | 在 for 循环语句的第三个表达式中使用的变量"j"没有在第二个表达式中被判断 |
| 分 析 | 规范中 for 循环语句中用到的变量都应该在第二个表达式中判断 |
| 解 决 | 这里要十分小心新增变量的判断,根据实际情况判断如何增加判断条件 |
这里可以看出 j 的作用是为了让 数组[j] = 数组[i] ,且从上面的判断可以看出,循环的结束条件主要由 i 来判断。所以,我新增条件为给数组做防护,防止数组越界,新增条件为 j < 数组_max (这里要去查找数组定义的最大值)
问题类型 4
| 问 题 序号4 | switch 语句中最后必须为 default 子句 (MISRA2004-15_3-3) |
|---|---|
| 提 示 | switch 语句中没有含有default 子句 [行 1117] |
| 分 析 | 规范中所有switch 语句必须以 default 语句结束 |
| 解 决 | 在缺少的部分增加default 语句 |
加句话而已,就不解释了。
问题类型 5
| 问 题 序号5 | 不允许其结果是不变的布尔运算 (MISRA2004-13_7_t-3) |
|---|---|
| 提 示 | 包含枚举常量的条件始终为"true"或"false" [行 1438] |
| 分 析 | 规范中枚举型变量在条件运算中不能大于最小值、小于最大值(恒为true 或 false) |
| 解 决 | 可以删除这一部分,这一般是为了做防护的 |
案例如图所示。
问题类型 6
| 问 题 序号6 | 不能将typedef的名字重用在另一个typedef名字 (MISRA2004-5_3_b-3) |
|---|---|
| 提 示 | 不能将typedef "s098p_SysPanicHandler"的名字重用在另一个typedef名字[行 270] |
| 分 析 | 规范中所有switch 语句必须以 default 语句结束 |
| 解 决 | 在缺少的部分增加default 语句 |
问题类型 7
| 问 题 序号7 | 不能重用typedf名字 (MISRA2004-5_3_a-3) |
|---|---|
| 提 示 | 类型定义得到的名字不允许被重用:speedStep [行 2229] |
| 分 析 | 规范中不允许存在类似与 int int[ ] ,第二个int就属于类型定义的重用 |
| 解 决 | 更改类型或者变量的名字(这里建议搜索整个工程,谁被调用的少就更改谁) |
问题类型 8
| 问 题 序号8 | 不要使用可变数量的参数来定义函数 (MISRA2004-16_1-3) |
|---|---|
| 提 示 | 不要使用#include<stdarg.h> |
| 分 析 | #include<stdarg.h>头文件中,包含一些可变参数的函数,规范中,可变的函数参数会带来隐患,企业编码中应该尽量不使用这种函数。 |
| 解 决 | 我的工程中是在调试代码中含有,所以就不进行处理了 |
问题类型 8
| 问 题 序号8 | 不要使用可变数量的参数来定义函数 (MISRA2004-16_1-3) |
|---|---|
| 提 示 | 发现使用宏 va_start [行 166] |
| 分 析 | 问题同上,使用了可变参数的函数 |
| 解 决 |
#include<stdarg.h>
int main()
{
va_start(valist, numb); 取numb个参数放到valist地址
}
问题类型 9
| 问 题 序号9 | 函数不应该直接或者间接地调用自己 (MISRA2004-16_2-3) |
|---|---|
| 提 示 | 函数“xxxxxxxxx”被递归调用 |
| 分 析 | 这里要看自己的代码怎么写的了,应该尽量删除递归调用 |
| 解 决 |
问题类型 10
| 问 题 序号10 | 函数的声明与原型的类型必须一致 (MISRA2004-8_3_b-3) |
|---|---|
| 提 示 | 声明中的参数类型与实现中的"winSysLog"不一样[行 309] |
| 分 析 | 这里要看自己的代码怎么写的了,应该尽量删除递归调用 |
| 解 决 |
问题类型 11
| 问 题 序号11 | 在内部范围的标识符不能和外部的标识符用同样的名字,因为会隐藏那个标识符 (MISRA2004-5_2_b-3) |
|---|---|
| 提 示 | 变量 j 在外层作用域里也被声明了[行 309] |
| 分 析 | 这里的意思是在一个函数里面j 被重复定义了,这里就必须讲到一点,j、i的定义必须定义在作用域内,如下代码。 |
| 解 决 | 删除定义域外的定义 |
#include<stdarg.h>
int main()
{
int i;
int j; //在vc++6.0中 j应该定义在它的作用域内,而不是这里。
for(i=0;i<100;i++)
{
int j; //j应该定义在这里
for(j=0;j<100;j++)
{
}
}
}
问题类型 12
| 问 题 序号12 | 在循环体中至多只允许一个用于结束循环的break语句(MISRA2004-14_6-3) |
|---|---|
| 提 示 | 此循环在下列行中包含多个"break" |
| 分 析 | 一个循环体最好只有一个break语句,当有多个break语句时,会导致break语句之后的代码无法正常执行(假如有处理操作的话) |
| 解 决 | 把break语句条件合并、重新写一个循环,把两个条件分开。 |
#include<stdarg.h>
void foo( ) {
int a;
for (a = 0; a < 10; a++) /* 错误 */
{
if (a == 5)
{
break;
}
if (a == 7)
{
break;
}
}
}
问题类型 13
| 问 题 序号13 | 在数组和结构提的非0初始化中,使用大括号进行标识和匹配 (MISRA2004-9_2-3) |
|---|---|
| 提 示 | 并非所有变量“Xxxxxx”的元素都已初始化 |
| 分 析 | 结构或数组中存在没有初始化的元素,初始化应该对里面的所有元素进行初始化 |
| 解 决 | 看看对应的地方是否每个元素都被初始化 |
问题类型 13
| 问 题 序号13 | 在数组和结构提的非0初始化中,使用大括号进行标识和匹配 (MISRA2004-9_2-3) |
|---|---|
| 提 示 | 应使用其他大括号来指示嵌套结构 |
| 分 析 | 这个问题的意思是类似与二维数组或结构体数组,应该用大括号把第一层的元素框起来,有点不好解释,看下下列代码,简明易懂。 |
| 解 决 | 按规则进行初始化 |
#include<stdarg.h>
void foo( )
{
int y[3][2] = { 1, 2, 3, 4, 5, 6 }; // Violation
struct S
{
int i;
struct T
{
int j;
}t;
} s = {1, 2}; // Violation
}
//正确如下
int y[3][2] = { { 1, 2 }, { 3, 4 }, { 5, 6 } }; // OK
struct S {
int i;
struct T {
int j;
}t;
} s = {1, { 2 }}; // OK
问题类型 14
| 问 题 序号14 | 如果函数返回错误信息,该错误信息必须经过测试 (MISRA2004-16_10-3) |
|---|---|
| 提 示 | 没有使用函数的"XXXXXXXXX"返回值 |
| 分 析 | 有返回值的函数应该使用该返回值,否则应该定义为void型,以防错误使用返回值。 |
| 解 决 | 有返回值应该使用,没有的话应该把函数定义为void型 |
#include<stdarg.h>
int SomeFunctionReturningError( );
void foo( )
{
SomeFunctionReturningError( ); // 违规,没有调用返回值
}
//修改如下
int SomeFunctionReturningError( );
int foo( )
{
int x;
x = SomeFunctionReturningError( ); // OK
(void)SomeFunctionReturningError( ); //OK
if (SomeFunctionReturningError( )); // OK
switch (SomeFunctionReturningError( )) { // OK
}
return SomeFunctionReturningError( ); // OK
}
问题类型 15
| 问 题 序号15 | 如果对无符号字符型或无符号短整形进行~和<<位运算后,其结果应立即强制转换成操作数的基本类型 (MISRA2004-10_5-3) |
|---|---|
| 提 示 | [a<<b]运算结果应该强制转换为XXXXX类型 [行 546] |
| 分 析 | 进行位运算时,应该注意转换的数据类型,以防止转换的时候越界。 |
| 解 决 | 把break语句条件合并、重新写一个循环,把两个条件分开。 |
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
void foo()
{
uint8_t port = 0x5aU;
uint8_t result_8;
uint16_t result_16;
uint16_t mode;
result_8 = (~port) >> 4;/* 违规 */
result_16 = ((port << 4) & mode) >> 6;/* 违规 */
}
//修改如下
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
void foo()
{
uint8_t port = 0x5aU;
uint8_t result_8;
uint16_t result_16;
uint16_t mode;
result_8 = ((uint8_t)(~port)) >> 4 ;/* 修改为*/
result_16 = ( (uint16_t) (~(uint16_t)port)) >> 4 ; /* 修改为*/
}
问题类型 16
| 问 题 序号16 | 带有自动存储空间的对象地址不能被分配给另一个对象,该对象销毁后,另一个对象仍然存在,但指针成为野指针 (MISRA2004-17_6-3) |
|---|---|
| 提 示 | 函数调用自动变量的地址,函数存储一个静态或者全局变量的地址 |
| 分 析 | 有返回值的函数应该使用该返回值,否则应该定义为void型,以防错误使用返回值。 |
| 解 决 | 把break语句条件合并、重新写一个循环,把两个条件分开。 |
#include<stdarg.h>
void foo( )
{
int y[3][2] = { 1, 2, 3, 4, 5, 6 }; // Violation
struct S
{
int i;
struct T
{
int j;
}t;
} s = {1, 2}; // Violation
}
问题类型 16
| 问 题 序号16 | 带有自动存储空间的对象地址不能被分配给另一个对象,该对象销毁后,另一个对象仍然存在,但指针成为野指针 (MISRA2004-17_6-3) |
|---|---|
| 提 示 | 函数调用自动变量的地址,函数存储一个静态或者全局变量的地址 |
| 分 析 | 如果一个局部变量的地址被赋值给另外一个大范围的局部变量,或者一个静态变量,或者从一个函数中返回,然后包含这个地址的变量的生存周期可能超过最初变量的生存周期(它的地址变得无效)。 |
| 解 决 | 不要将局部变量的地址赋值给全局变量、静态变量或者函数的返回值 |
int* global;
int* foo() {
int iLocal;
static int* siLocal;
siLocal = &iLocal; //赋值给静态变量
global = &iLocal; // 赋值给全局变量
return &iLocal; // 赋值给返回值
}
问题类型 17
| 问 题 序号17 | 必须以break作为switch每个子句的结束语句 (MISRA2004-15_2-3) |
|---|---|
| 提 示 | 每一个switch语句的非空分支应当用一个break语句终止 |
| 分 析 | 防止switch在跳转分支之后继续执行下去 |
| 解 决 | 添加break语句 |
问题类型 18
| 问 题 序号18 | 所有的"if…else-if"结构中应该由"else"分支结束 (MISRA2004-14_10-3) |
|---|---|
| 提 示 | 在最后的最后的"else-if"结构之后提供"else"分支 [行 784] |
| 分 析 | 确保代码数据流的正确性、提高代码的可读性和可维护性 |
| 解 决 | 添加break语句 |
void foo(int a)
{
if(a > 0)
{
}
else if (a > 10) // Violation
{
}
}
未完待续
全部评论 (0)
还没有任何评论哟~

如图,我的工程中是有定义的,仔细看Ctrl_Temp_Match_Data.c引用的头文件没有包含kcg_imported_functions.h文件,所以进行添加。
这里改成
加句话而已,就不解释了。
案例如图所示。