MISRA C:2004,误移位

时间:2012-02-04 01:47:13

标签: c unsigned iar integer-promotion misra

我正在使用IAR Workbench编译器和MISRA C:2004检查 片段是:

#define UNS_32 unsigned int
UNS_32 arg = 3U;
UNS_32 converted_arg = (UNS_32) arg;
/* Error line --> */ UNS_32 irq_source = (UNS_32)(1U << converted_arg);

MISRA错误是: 错误[Pm136]:从基础MISRA类型“unsigned char”到“unsigned int”的非法显式转换(MISRA C 2004规则10.3)

我在上面的任何代码中都没有看到任何unsigned char

Why did Misra throw an error here?的讨论讨论了乘法,它可能有不同的推动规则,而不是左移。

我的理解是编译器应该将表达式提升为更大的数据类型,而不是降级为更小的数据。

这里到底发生了什么?

如何使代码符合MISRA C:2004标准?

编辑1:

将错误行更改为:

UNS_32 irq_source = (UNS_32)((UNS_32) 1U << converted_arg);  

会使错误消失。

3 个答案:

答案 0 :(得分:4)

第二个问题:

  

如何使代码符合MISRA C:2004标准?

您可以按照MISRA标准的方式编写此内容,如下所示:

typedef unsigned int UNS_32;

UNS_32 test(void);
UNS_32 test(void)
{
  UNS_32 original_arg = 3U;
  UNS_32 irq_source = 1UL << original_arg;

  return irq_source;
}

回到第一个问题:

  

这里到底发生了什么?

第一条规则10.3规定复数整数表达式不得转换为比基础类型更宽的类型。

理解错误消息的一个关键是概念底层类型,这是一个特定于MISRA-C的概念。简而言之,常量的基础类型是它可以适应的最小类型。在这种情况下,1U具有基础类型unsigned char,尽管其语言类型为unsigned int

10.3规则背后的基本原理是避免在大于部分的上下文中使用操作结果的情况。标准示例是乘法,其中alphabeta是16位类型:

uint32_t res = alpha * beta;

这里,如果int是16位,则乘法将以16位执行,然后结果将转换为32位。另一方面,如果int是32位或更大,则乘法将以更大的精度执行。具体地说,这将使得乘法时的结果不同,比如0x4000和0x10。

MISRA规则10.3通过强制执行将转换结果放在临时中来解决此问题,后者将转换为更大的类型。这样你就不得不以某种方式编写代码。

如果打算使用16位乘法:

uint16_t tmp = alpha * beta;
uint32_t res = tmp;

另一方面,如果意图是32位乘法:

UNS_32 res = (UNS_32)alpha * (UNS_32)beta;

因此,在这种情况下,表达式1U << count是潜在的问题。如果converted_arg大于16位,则在使用16位int时可能会出现问题。但是,MISRA允许您撰写1UL << count(UNS_32)((UNS_32)1U << original_arg)。你提到MISRA检查员在后一种情况下发出错误 - 我的不是这样,请再次检查。

所以,我看到它的方式,你正确使用的MISRA C检查器发现违反了规则10.3。

答案 1 :(得分:3)

在MISRA规则指定的C89中,后缀为U的整数常量的类型是列表“unsigned int,unsigned long int”的第一个,其中值可以表示。这意味着1U的类型必须为unsigned int

按位移位运算符的定义指定对每个操作数执行整数提升(这不会更改unsigned int),并且结果的类型是提升的左操作数的类型。在这种情况下,(1U << converted_arg)的结果类型为unsigned int

此处唯一的显式转换是将此unsigned int值转换为unsigned int,因此这必须是编译器警告的内容 - 尽管看不到unsigned char,意味着检查器似乎是错误的。

从技术上讲,从unsigned intunsigned int的强制转换似乎违反了规则10.3,该规则说“复杂表达”的结果只能转换为更窄类型 - 并且转换为相同的类型显然不会转换为更窄的类型。

演员是不必要的 - 我只是省略它。

答案 2 :(得分:0)

在MISRA的早期,有时将其应用到的程序针对行为不符合尚未发布的C89标准的编译器。在发明了C的机器上,对16位值进行操作的成本与对8位值进行操作的成本相同。与直接对char值执行算术运算相比,将int值提升为char并在存储回char时截断结果实际上更便宜,更容易。 C标准一经发布,将强制所有 all C实现必须将所有整数值提升为至少可以容纳-32767..32767范围的int类型或{ {1}}类型可以容纳至少0..65535,或者更大的类型(1980年代针对8位计算机的编译器)并不总是这样做。

尽管如今尝试使用不能满足这些要求的C编译器似乎很疯狂,但是1980年代的程序员通常会面临选择使用“ C ish”编译器还是用汇编语言编写一切的选择。 MISRA中的某些规则(包括“固有类型”规则)旨在确保程序即使在将unsigned视为8位类型的怪异实现上运行也能正常工作。