1

我正在尝试修复其他人编写的模块的 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;
4

2 回答 2

7

增加枚举是错误的!

枚举被添加到语言中,作为 #define 的更好替代方法用于许多常量,并且在其他方​​面被视为整数(即​​整数的 const 数组)。要执行更多操作,则需要运行时检查。

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

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

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

于 2019-09-10T10:35:37.997 回答
2

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

如果您有一些增加枚举的代码,并且它运行良好(在所有条件下),那只是因为许多互锁的假设和约定可能没有全部写下来,而且几乎肯定不会很明显(并获得荣誉)后来的维护程序员。

因此,确实,没有简单的解决方法。任何简单的修复(可能会让您的 MISRA 检查器关闭)都可能使实践中的固有风险保持不变——也就是说,您可能满足 MISRA 的文字,但不满足精神(这显然是倒退的)。

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

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

  1. 使用 anint和一些#defined 常量。
  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 ... ;
        }
    }

使用 a 的优点switch是,如果您将枚举值留在这样的开关之外,现代编译器会警告您。

于 2019-09-10T11:59:13.927 回答