运算符“++”的未经许可的操作数 [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 这样的标准的全部意义在于避免有风险的代码。毫无疑问,递增枚举是有风险的。
如果你有一些递增枚举的代码,并且它运行良好(在所有条件下),这只是因为许多互锁的假设和约定可能是这一切都写下来了,而且几乎肯定不会让后来的维护程序员明白(并受到尊重)。
因此,确实没有简单的解决方法。
所以是的,您应该要求(而不仅仅是建议)模块所有者更改实现。
修改后的实施会是什么样子?我认为应该具有以下一个或多个方面:
- 使用一个
int
和一些 #define
d 常量。
- 有一个单独的封装函数来从一个状态映射到下一个状态。
- 使用显式转换 table 将一个状态映射到下一个状态。
- 如果有大量的状态,并且如果它们中的大多数按顺序排列,那么 +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
的优点是,如果您在这样的开关中遗漏枚举值,现代编译器会警告您。
我正在尝试修复其他人编写的模块的 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 这样的标准的全部意义在于避免有风险的代码。毫无疑问,递增枚举是有风险的。
如果你有一些递增枚举的代码,并且它运行良好(在所有条件下),这只是因为许多互锁的假设和约定可能是这一切都写下来了,而且几乎肯定不会让后来的维护程序员明白(并受到尊重)。
因此,确实没有简单的解决方法。
所以是的,您应该要求(而不仅仅是建议)模块所有者更改实现。
修改后的实施会是什么样子?我认为应该具有以下一个或多个方面:
- 使用一个
int
和一些#define
d 常量。 - 有一个单独的封装函数来从一个状态映射到下一个状态。
- 使用显式转换 table 将一个状态映射到下一个状态。
- 如果有大量的状态,并且如果它们中的大多数按顺序排列,那么 +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
的优点是,如果您在这样的开关中遗漏枚举值,现代编译器会警告您。