C语言 违反 MISRA 2012 - 类型不匹配(规则 10.1、10.4)



我正面临无法理解的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 ints. 应该没有问题(PC-Lint 通过第一个 OR 操作并抱怨第二个操作!!

规则10.4规定,操作的操作数应具有相同的基本类型。

我不明白为什么存在这些违规行为,尽管所有操作数都声明为uint8_t

我尝试在每两个 OR 常量周围加上括号。我也尝试过将它们全部投射到uint8_tvolatile uint8_t。未解决违规问题。

我检查了这两个帖子(1,2(,但它们没有回答我的问题。

I2C_CCRH_FS | I2C_CCRH_DUTY本身就符合MISRA。这两个操作数基本上都是无符号的,因此子表达式很好。但是,每个操作数仍然隐式转换为int。实践中的结果是int型。

在伪代码中:当你做(result as int) | I2C_CCRH_CCR时,隐式提升前的操作数的类型int | uint8_tuint8_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 ) );

最新更新