在 core_cm4.h 上,为什么会有类似 ((uint32_t)(int32_t)IRQn) 的转换?

On core_cm4.h why is there casting like ((uint32_t)(int32_t)IRQn)?

在来自 core_cm4.h 的以下代码中,为什么会有双重转换 ((uint32_t)(int32_t)IRQn)

例如在下面的函数中:

__STATIC_INLINE void NVIC_EnableIRQ(IRQn_Type IRQn)
{
  NVIC->ISER[(((uint32_t)(int32_t)IRQn) >> 5UL)] = (uint32_t)(1UL << (((uint32_t)(int32_t)IRQn) & 0x1FUL));
}

这样做的目的是什么?

由于 CM4 软件为核心实现了负中断源,您必须先将值转换为 32 位有符号整数,然后再转换为无符号 32 位,以便在数字左侧填充零进行适当的右移。

CM4 使用 -15 到 -1 作为 CM4-Core 源,从 0 到下一个作为供应商特定源。

假设IRQn是你说的范围内的一个整数(任何有符号整数类型),那么(uint32_t)(int32_t)IRQn(uint32_t)IRQn.

是完全一样的

代码作者可能没有理解C类型转换规则;这是基于价值观,而不是陈述。例如,将 -3 转换为 uint32_t 总是得到 UINT32_MAX - 2,而不管 -3 是哪种数据类型。重要的是 "negative 3" 的值。

(另一个答案解释了使用强制转换和完全不强制转换之间的区别)。

通过查看该片段,很明显无论编写它的人对隐式类型提升规则的工作原理感到困惑。此外,>> 5UL 看起来很可疑,当我看到它时,我立即怀疑这个代码库对 MISRA-C 的理解不足;可能他们使用的是错误的静态分析器,会吐出误报。

访问Github证明我的怀疑是正确的,有评论说打算遵循MISRA-C:2004。

MISRA-C 要求不得发生危险的隐式类型提升。这些转换是一些试图使静态分析器静音的失败尝试,但不了解该工具为何发出这些警告。

正确的,符合 MISRA-C(2004 和 2012)的代码应该是这样的:

NVIC->ISER[((uint32_t)IRQn>>5UL)] = (1UL << ((uint32_t)IRQn & 0x1FUL));

(MISRA-C 要求复杂的子表达式必须使用括号,不管有什么运算符优先级。)

这段代码仍然很乱,因此最好将其重写以提高可读性,使其生成完全相同的机器代码:

uint32_t ISER_index = ((uint32_t)IRQn >> 5UL);
uint32_t shift_n    = ((uint32_t)IRQn & 0x1FUL);
NVIC->ISER[ISER_index] = (1UL << shift_n);

旁注:
MISRA-C:2004 要求移位表达式的结果应立即转换为 "underlying type"(MISRA 术语)。因此,也可以编写 (IRQn_Type)(IRQn >> 5UL),它仍然符合 MISRA-C:2004。

但是,MISRA 中没有任何内容阻止您在转换之前将自己的转换添加到不同的类型,例如 uint32_t。这是一个更好的做法,因为它完全消除了隐式提升。


对于那些对 C 中的隐式类型提升如何工作感到困惑的人,请参阅