MISRA C:类型转换和 <<、& 位运算符错误

MISRA C: Type casting and <<, & bit-wise operators error

在执行 << 和 & 操作时,我在使用 MISRA 时遇到了很多麻烦。

上下文类型的定义

typedef uint32_t uint32;
typedef volatile uint32* canreg_txrx_pointer;
canreg_txrx_pointer canreg_txrx;
uint8_t valueRead, value;
#define CAN_DIR_BIT_POS ((uint8_t)2U)
#define CAN_OUT_BIT_POS ((uint8_t)1U)
#define CAN_IN_BIT_POS ((uint8_t)0U)

尝试对寄存器执行一系列 << 和 & 操作时,我不断收到错误 #1397-D (MISRA-C:2004 10.5/R) If the bitwise operators ~ and << are applied to an operand of underlying type unsigned char or unsigned short, the result shall be immediately cast to the underlying type of the operand

下面给出了一个未通过 misra 检查的示例和一个通过的示例

*canreg_txrx = (uint32)((*canreg_txrx & (~(uint32)(1U << CAN_OUT_BIT_POS))) | (uint32)(value << CAN_OUT_BIT_POS)); //doesn't pass MISRA
valueRead = (uint8_t)(((uint8_t)*regToOperateOn & (uint8_t)(1U << CAN_OUT_BIT_POS)) >> CAN_OUT_BIT_POS); //passes MISRA

我已经尝试对有问题的线路进行多次更改,其中 none 导致 MISRA 让它通过(除了随机的,它只是没有给我任何警告)到我决定投射所有东西的地步这仍然不起作用

*canreg_txrx = (uint32)((*canreg_txrx & (uint32)(~(uint32)((uint32)1U << (uint32)CAN_OUT_BIT_POS))) | (uint32)((uint32)value << (uint32)CAN_OUT_BIT_POS)); //doesn't pass MISRA

我真的很困惑如何修复这段代码,使其通过 MISRA,因为这种按位逻辑在多个地方使用,但所有地方都没有通过。我认为如果所有内容都转换为相同类型那么应该没有问题,因为它们都以相同类型运行但由于某些原因不起作用。

我从未使用过 Misra,但我发现您的代码因 typedef、转换和括号的噪音而难以阅读。我的建议是按照以下思路将问题简化并拆分为更小的部分:

*canreg_txrx = (uint32)((*canreg_txrx & (~(uint32)(1U << CAN_OUT_BIT_POS))) | (uint32)(value << CAN_OUT_BIT_POS)); // =>

*canreg_txrx &= ~(1U << CAN_OUT_BIT_POS);
*canreg_txrx |= value << CAN_OUT_BIT_POS; // maybe =>

#define BIT_CLEAR(val, pos) ((val) &= ~(1U << (pos)))
BIT_CLEAR(*canreg_txrx, CAN_OUT_BIT_POS);
*canreg_txrx |= value << CAN_OUT_BIT_POS; // *

'*' 如果value是bit,那么这个就bit set了,不然想不出好名字。

然后 运行 它通过 Misra 和 解决它抱怨的问题。

MISRA-C 有点混乱,因为它坚持转换为“底层类型”(MISRA-C:2004 术语) 转换完成后。我更喜欢在 转换之前转换为大型无符号类型 ,因为这样可以排除所有可能发生的问题。

这些规则力图防止的是通过 意外转换为有符号类型,然后进行位移之类的操作,这可能很危险并会导致各种错误,包括签名更改、意外符号扩展,左移有符号值时的未定义行为,impl.defined 右移它们时的行为,依此类推。

在你的例子中,你之所以会遇到这些问题,是因为你没有将 ~ 的结果转换为此处的基础类型:~(uint32)(1U << CAN_OUT_BIT_POS))。您的静态分析器似乎给出了误报,因为此处的基础类型似乎是 uint32_t。没有更小的类型可以考虑,除非你似乎有 32 位整数(因为你有 32 位 CAN 寄存器),但该工具可能不知道你的整数是 32 位,在这种情况下 1U 可以一直是 16 位值。

否则,该工具可能会变得混乱,因为您在定义这些宏时使用了混淆的强制转换...但是对于移位运算符来说,正确的操作数的类型并不重要(尽管它也被提升了)。

在这种特殊情况下,none 这应该是危险的,因为您期望 unsigned 32。警告、强制转换,一切 - 它只是噪音,使您的代码变得脆弱和不可读。

我建议您通过以下方式挽救代码:

  • 从宏中删除所有 uint8 转换,那只是噪音,可能会导致问题。
  • 摆脱自制的 uint32 等类型并使用标准 C uint32_ttypedef uint32_t uint32; 是任何 C 程序中都不应该存在的烂代码。使用现有的行业标准,不要发明新的。
  • 不要在 typedef 后面隐藏指针,这是非常危险的做法,会使代码难以阅读。
  • 去掉多余的转换。假设这是一个 32 bitter,你不应该将 1U 类型转换为 uint32_t 因为它们已经是相同的类型了。
  • 将不可读的行分解成几个表达式。例如我们可以做 const uint32_t mask = ~(1U << CAN_OUT_BIT_POS);。应该不需要强制转换,因为基础类型已经是 uint32_t。你的 MISRA 检查器可能太笨了,无法意识到你有 32 位整数,所以你可能必须添加一个转换 (uint32_t)~ ...。不是为了 MISRA-C:2004 合规性,而是为了让一个潜在的愚蠢工具静音。
  • 如果您只需要施法以使特定工具静音,您应该对此做出明确的评论:/* this cast just to silence BrokenAnalyser from Bugworks */。表明您知道自己在做什么,并将误报与实际必要的演员表分开。
  • MISRA-C 的另一个有效问题是您不应该在同一个表达式中对 volatile 寄存器进行多次访问,因为这会引入未排序的副作用,而且您无法真正分辨何时和在该行实际寄存器访问发生的位置,加上它阻止编译器优化。这是您尚未发现的另一个 MISRA 违规行为。这也可以通过将长表达式分解成几个来解决。

一个例子:

const uint32_t mask = ~(1U << CAN_OUT_BIT_POS);
uint32_t txrx = *canreg_txrx; // volatile access on a line of its own
txrx = (txrx & mask) | ((uint32_t)value << CAN_OUT_BIT_POS);
*canreg_txrx = txrx;

中间的 txrx 行将得到优化,并且在机器代码中您只会得到一些指令。据我所知,这应该符合 MISRA-C:2004 和 2012 标准。这也不会转移隐式提升的 value 然后再转换。