在core_cm4.h上为什么会像((uint32_t)(int32_t)IRQn那样)?

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

例如,在以下function中:

 __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核心源,从0到下一个作为供应商特定源。

通过查看该片段,很明显无论是谁编写它都会对隐式类型推广规则的工作方式感到困惑。 另外, >> 5UL看起来非常可疑,当我看到我立即怀疑这个代码库对MISRA-C的理解不足时; 可能他们正在使用一个坏的静态分析仪,它会吐出误报。

在Github的访问certificate了我的怀疑是正确的,有评论表明其目的是遵循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要求将class次表达的结果立即转换为“基础类型”(MISRA术语)。 因此,人们也可以写(IRQn_Type)(IRQn >> 5UL) ,它仍然符合MISRA-C:2004标准。

但是,MISRA中没有任何内容阻止您在class次之前将自己的演员阵容添加到其他类型,例如uint32_t 。 这是一件更好的事情,因为它完全消除了隐含的促销。


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

假设IRQn是您所说范围内的整数(任何有符号整数类型),则(uint32_t)(int32_t)IRQn(uint32_t)IRQn完全相同。

代码的作者可能不了解C类型转换规则; 它们基于价值而非表示。 例如,将-3转换为uint32_t总是给出UINT32_MAX - 2无论-3是哪种数据类型。 重要的是“负3”的价值。

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