我正面临无法理解的MISRA C 2012违规。以下是代码:
#define I2C_CCRH_FS ((uint8_t)0x80)
#define I2C_CCRH_DUTY ((uint8_t)0x40)
#define I2C_CCRH_CCR ((uint8_t)0x0F)
typedef struct I2C_struct
{
volatile uint8_t CR1;
volatile uint8_t CR2;
volatile uint8_t CCRL;
volatile uint8_t CCRH;
} I2C_TypeDef;
#define I2C_BaseAddress 0x5210
#define I2C ((I2C_TypeDef *) I2C_BaseAddress)
I2C->CCRH &= ~(uint8_t)((I2C_CCRH_FS | I2C_CCRH_DUTY) | I2C_CCRH_CCR);
在前面的代码中,PC-Lint 抱怨:
Unpermitted operand to operator '|' [MISRA 2012 Rule 10.1, required]
Mismatched essential type categories for binary operand [MISRA 2012 Rule 10.4, required]
规则 10.1 规定 ORingunsigned int
s. 应该没有问题(PC-Lint 通过第一个 OR 操作并抱怨第二个操作!!
规则10.4规定,操作的操作数应具有相同的基本类型。
我不明白为什么存在这些违规行为,尽管所有操作数都声明为uint8_t
?
我尝试在每两个 OR 常量周围加上括号。我也尝试过将它们全部投射到uint8_t
和volatile uint8_t
。未解决违规问题。
我检查了这两个帖子(1,2(,但它们没有回答我的问题。
I2C_CCRH_FS | I2C_CCRH_DUTY
本身就符合MISRA。这两个操作数基本上都是无符号的,因此子表达式很好。但是,每个操作数仍然隐式转换为int
。实践中的结果是int
型。
在伪代码中:当你做(result as int) | I2C_CCRH_CCR
时,隐式提升前的操作数的类型int | uint8_t
。uint8_t
也将在此处将整数提升为int
。您具有不同符号的操作数。
(我猜该工具抱怨 10.4,因为整数提升是通常算术对话的一部分,这就是 10.4 的意义所在。
这整个表达在实践中不会造成任何问题,因此警告大多是迂腐的。但是想象一下,如果你在没有强制转换的情况下完成了~(I2C_CCRH_FS | I2C_CCRH_DUTY) | I2C_CCRH_CCR)
- 你最终会得到一个负数,类似于 2 补码中表达的0xFFFFFFxx
。那将是潜在的危险。
要解决此问题,您有两种选择:
- 对于每个操作,将结果转换回预期类型。这通常是MISRA-C的精神。 在操作
- 之前将操作数强制转换为大型无符号类型。通常更具可读性 IMO。
另请注意,~
运算符不应与已签名的操作数一起使用!这违反了细则10.1。回uint8_t
的演员应该最后完成。
博士如何使代码符合MISRA标准:
你要么必须做一些像这样半糟糕的事情:
I2C->CCRH &= (uint8_t) ~ (uint8_t) ((uint8_t)(I2C_CCRH_FS | I2C_CCRH_DUTY) | I2C_CCRH_CCR)
这有点乱。我宁愿提前投。假设 32 位 CPU:
I2C->CCRH &= (uint8_t) ~( (uint32_t)I2C_CCRH_FS | // comment explaining FS
(uint32_t)I2C_CCRH_DUTY) | // comment explaining DUTY
(uint32_t)I2C_CCRH_CCR ); // comment explaining CCR
上述样式在处理MCU寄存器等时很有用。此代码是可以接受的,但可以进一步简化。
如果可以将定义更改为#define I2C_CCRH_FS 0x80u
,则得到:
I2C->CCRH &= (uint8_t) ~(I2C_CCRH_FS | I2C_CCRH_DUTY | I2C_CCRH_CCR);
它仍然符合MISRA,因为MISRA喜欢方便的小u
后缀。
执行按位运算时(I2C_CCRH_FS | I2C_CCRH_DUTY)
结果将提升为整数。在此处查看整数升级规则
因此,上述操作的结果与下一个按位 OR| I2C_CCRH_CCR
要解决此问题,您需要将强制转换添加到按位 OR 操作的结果中。
需要第一个强制转换才能将~
运算符的结果从int
转换回unsigned
I2C->CCRH &= (uint8_t)~(uint8_t)((uint8_t)(I2C_CCRH_FS | I2C_CCRH_DUTY) | I2C_CCRH_CCR);
解释
I2C->CCRH &= (uint8_t)~ // Required to cast result of ~ operator to uint8_t
(uint8_t) // Casts the result of the 2nd OR to uint8_t
((uint8_t) // Casts the result of the 1st OR
(I2C_CCRH_FS | I2C_CCRH_DUTY) // 1st OR
| I2C_CCRH_CCR); // 2nd OR
我建议你自己把事情搞复杂......您可以将#define
宏保留在unsigned int
:
#define I2C_CCRH_FS 0x80u // No need for cast, but add u suffix
#define I2C_CCRH_DUTY 0x40u // ditto
#define I2C_CCRH_CCR 0x0Fu // ditto
{snip}
#define I2C_BaseAddress 0x5210u // Add u suffix
// Note: this violates R.11.1 but a MISRA C Permit
// exists to help with the deviation
#define I2C ((I2C_TypeDef *) I2C_BaseAddress)
// Only one cast required, to bring into 8 bit
I2C->CCRH &= (uint8_t)( ~( I2C_CCRH_FS | I2C_CCRH_DUTY | I2C_CCRH_CCR ) );