运算符“++”的未经许可的操作数 [MISRA 2012 规则 10.1,必需]

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

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

我提到了 SE question,它讨论了同一主题。我该如何解决这个错误?我需要建议模块所有者更改实现吗?

#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,这可能意味着您的值之后不对应于枚举中的任何值。

所以我的建议是 "don't do it" 即使特定的编译器允许您这样做。将其重写为明确的内容。

如果你想在由连续整数表示的特定状态范围内循环,你可以使用枚举,但前提是你使它的值也连续。对解释解释不要修补的定义提出很多警告。然后增加一个表示状态的整数,然后可以安全地将其与枚举进行比较。

使用像 MISRA 这样的标准的全部意义在于避免有风险的代码。毫无疑问,递增枚举是有风险的。

如果你有一些递增枚举的代码,并且它运行良好(在所有条件下),这只是因为许多互锁的假设和约定可能是这一切都写下来了,而且几乎肯定不会让后来的维护程序员明白(并受到尊重)。

因此,确实没有简单的解决方法。

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

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

  1. 使用一个 int 和一些 #defined 常量。
  2. 有一个单独的封装函数来从一个状态映射到下一个状态。
  3. 使用显式转换 table 将一个状态映射到下一个状态。
  4. 如果有大量的状态,并且如果它们中的大多数按顺序排列,那么 +1 增量将很好地封装它(比一堆任意的状态转换更干净可靠),继续并使用 +1 增量,but 和一些附带的断言以确保各种假设成立。例如:
    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 的优点是,如果您在这样的开关中遗漏枚举值,现代编译器会警告您。