繁体   English   中英

运算符“ ++”的不允许操作数[MISRA 2012规则10.1,必填]

[英]Unpermitted operand to operator '++' [MISRA 2012 Rule 10.1, required]

我正在尝试为其他人编写的模块修复Misra警告。 我观察到在enum上使用了++操作。

我提到了关于同一主题的SE问题 如何解决此错误? 我是否需要建议模块所有者来更改实现?

#include <stdio.h>

typedef enum
{  
    COMPARE = 0,
    INCONSISTENT = 10,
    WRITE,
    READ,
    FINISHED  
}TestsType;

static TestsType CurrentTest;

void fun1(void)
{

    if(READ != CurrentTest)
    {

            CurrentTest++;
    }
    else
    {
            CurrentTest = FINISHED;
    }
}

int main(void) {
    // your code goes here

    CurrentTest = COMPARE;
    fun1();
    printf("%d", CurrentTest);

    return 0;
}

我故意将这样的enum保留在代码中以了解任何影响。 但是,在实际代码中,如下所示。

typedef enum
{  
        COMPARE,
        INCONSISTENT,
        WRITE,
        READ,
        FINISHED  
}TestsType;

增加一个枚举是错误的!

枚举被添加到语言中,作为对#define的更好的替代,它包含多个常量,并且在其他方​​面也被视为int(即,int的const数组)。 要强制执行其他任何操作,都需要运行时检查。

由于枚举值不必是连续的,因此当将它们视为整数时,将它们递增是没有意义的。 如果编译器确实允许它,它会认为它正在增加一个int,这可能意味着您的值此后与枚举中的任何值都不对应。

因此,即使特定的编译器允许您这样做,我的建议还是“不要这样做”。 将其重写为明确的内容。

如果要循环显示由连续整数表示的特定状态范围,则可以使用枚举,但前提是必须使其值也连续。 在定义上放很多警告,以解释不要修改。 然后增加一个表示状态的int,然后可以将其安全地与枚举进行比较。

使用诸如MISRA之类的标准的全部目的是避免风险代码 毫无疑问,但是增加枚举是有风险的。

如果您有一些可以递增枚举的代码,并且在所有条件下都可以正常工作,那仅仅是因为有许多相互关联的假设和惯例,这些假设和惯例可能并没有全部写下来,并且几乎可以肯定不会(并得到后来的维护程序员的尊重)。

因此,确实,没有简单的解决方案。 任何简单的修复(可能会使您的MISRA检查器关闭)都可以保留实践中的固有风险,也就是说,您可能会满足MISRA的要求,但不能满足要求(显然是落后的)。

因此,是的,您应该(不仅仅建议)要求模块所有者更改实现。

修改后的实现会是什么样子? 我认为它应该具有以下一个或多个方面:

  1. 使用一个int和一些#define d常量。
  2. 有一个单独的封装函数,可以从一种状态映射到另一种状态。
  3. 使用显式过渡表将一个状态映射到下一个状态。
  4. 如果存在大量状态,并且大多数状态依序进行,以致+1增量可以很好地封装该状态(比一堆任意状态转换更干净可靠),请继续使用+1增量, 附带一些断言以确保各种假设成立。 例如:
    enum state {
        OFF = 0,
        LOW = 3,
        MEDIUM,
        HIGH,
        EXCEPTIONAL = 10
    };

    /* States LOW..HIGH are assumed to be contiguous.  Make sure you keep them so! */
    /* If (and only if) you add or subtract states to the contiguous list, */
    /* make sure to also update N_CONTIGUOUS_STATES. */

    #define N_CONTIGUOUS_STATES 3

    enum state nextstate(enum state oldstate)
    {
        /* Normally performing arithmetic on enums is wrong. */
        /* We're doing so here in a careful, controlled, constrained way, */
        /* limited just to the values LOW..HIGH which we're calling "contiguous". */
        assert((int)LOW + N_CONTIGUOUS_STATES - 1 == (int)HIGH);
        if(oldstate >= LOW && oldstate < HIGH) {
            return (enum state)((int)oldstate + 1);
        } else {
            /* perform arbitrary mappings between other states */
        }
    }

这样做的目的是记录发生的情况,并确保如果以后的维护程序员以任何方式改变枚举定义,从而破坏假定存在某些允许连续递增的连续状态的假设,则断言将失败。

...但是我赶紧补充一点,这不是一个完整的解决方案。 保留的一个更重要的保证是处理每个状态转换,而且如果以后的维护程序员添加新状态但忘记更新转换映射,则更容易违反。 让编译器帮助您保证这一点的一种好方法是使用switch语句,尽管这将迫使您明确每个转换(即,不使用+1快捷键):

    enum state nextstate(enum state oldstate)
    {
        switch(oldstate) {
            case OFF: return ... ;
            case LOW: return MEDIUM;
            case MEDIUM: return HIGH;
            case HIGH: return ... ;
            case EXCEPTIONAL: return ... ;
        }
    }

使用switch的好处是,如果您在这样的开关中遗漏枚举值,现代编译器会警告您。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM