Advertisement

【(C++Test 代码静态分析问题修改(MISRA标准))】

阅读量:

@[TOC](C++Test 代码静态分析问题修改(MISRA标准))

文章目录

  • 问题类型
  • 问题类型案例分析与解决
  • 未完待续

问题类型

总表:有需要按序号进行搜索

  1. 函数在定义和调用之时必须始终有可见的原型 (MISRA-071-3)
  2. 外部对象不得声明在多个文件中 (MISRA-027-3)
  3. for 循环语句的三个表达式应该仅与循环控制相关 (MISRA2004-13_5-3)
  4. switch 语句中最后必须为 default 子句 (MISRA2004-15_3-3)
  5. 不允许其结果是不变的布尔运算 (MISRA2004-13_7_t-3)
  6. 不能将typedef的名字重用在另一个typedef名字 (MISRA2004-5_3_b-3)
  7. 不能重用typedf名字 (MISRA2004-5_3_a-3)
  8. 不要使用可变数量的参数来定义函数 (MISRA2004-16_1-3)
  9. 函数不应该直接或者间接地调用自己 (MISRA2004-16_2-3)
  10. 函数的声明与原型的类型必须一致 (MISRA2004-8_3_b-3)
  11. 在内部范围的标识符不能和外部的标识符用同样的名字,因为会隐藏那个标识符 (MISRA2004-5_2_b-3)
  12. 在循环体中至多只允许一个用于结束循环的brak语句(MISRA2004-14_6-3)
  13. 在数组和结构提的非0初始化中,使用大括号进行标识和匹配 (MISRA2004-9_2-3)
  14. 如果函数返回错误信息,该错误信息必须经过测试 (MISRA2004-16_10-3)
  15. 如果对无符号字符型或无符号短整形进行~和<<位运算后,其结果应立即强制转换成操作数的基本类型 (MISRA2004-10_5-3)
  16. 带有自动存储空间的对象地址不能被分配给另一个对象,该对象销毁后,另一个对象仍然存在,但指针成为野指针 (MISRA2004-17_6-3)
  17. 必须以break作为switch每个子句的结束语句 (MISRA2004-15_2-3)
  18. 所有的"if…else-if"结构中应该由"else"分支结束 (MISRA2004-14_10-3)

问题类型案例分析与解决


问题类型 1

问 题 序号1 函数在定义和调用之时必须始终有可见的原型(MISRA-071-3)
提 示 [行 116] 函数的原型 ’Main_Ctrl_Data_Temp_Match_Data‘没有先于函数的定义
分 析 由于函数未声明或声明在函数之后引起
解 决 1.先在项目工程中搜索该函数,确定没有在其他位置进行声明;2.在函数引用之前对函数进行声明;3.如果已在其他位置进行声明,对该头文件进行引用

问题1
如图,我的工程中是有定义的,仔细看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)

还没有任何评论哟~