在 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 到下一个作为供应商特定的源。
通过查看该片段,很明显,编写它的人对隐式类型提升规则的工作方式感到困惑。此外,它>> 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 要求将移位表达式的结果立即转换为“基础类型”(MISRA 术语)。因此,也可以编写(IRQn_Type)(IRQn >> 5UL)
并且它仍然符合 MISRA-C:2004。
但是,MISRA 中没有任何内容阻止您在班次之前将自己的演员表添加到不同的类型,例如uint32_t
。这是更好的做法,因为它完全消除了隐式提升。
对于那些对 C 中隐式类型提升如何工作感到困惑的人,请参阅隐式类型提升规则。
假设IRQn
是你所说的范围内的整数(任何有符号整数类型),则(uint32_t)(int32_t)IRQn
与(uint32_t)IRQn
.
代码作者可能不懂C类型转换规则;这是基于价值,而不是表示。例如,转换-3
为uint32_t
总是给出,UINT32_MAX - 2
无论是哪种数据类型-3
。重要的是“负 3”的值。
(另一个答案解释了使用演员表和完全不演员表之间的区别)。