4

我正在使用带有 MISRA C:2004 检查的 IAR Workbench 编译器。
片段是:

#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内容。

讨论为什么 Misra 会在这里抛出错误?讨论了可能具有与左移不同的提升规则的乘法。

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

这里到底发生了什么?

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

编辑1:

将错误行更改为:

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

不会使错误消失。

4

3 回答 3

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 charunsigned 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。

于 2012-02-06T10:24:10.537 回答
3

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

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

这里唯一的显式转换是将此unsigned int值强制转换为unsigned int,因此这一定是编译器警告的内容 - 尽管unsigned char看不到,这意味着检查器似乎有问题。

但从技术上讲,这种从unsigned intto的转换unsigned int似乎违反了规则 10.3,该规则说“复杂表达式”的结果只能转换为更窄的类型 - 转换为相同的类型显然不能转换为更窄的类型。

演员表是不必要的 - 我会简单地省略它。

于 2012-02-04T04:29:22.500 回答
0

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

尽管现在尝试使用无法满足这些要求的 C 编译器似乎很疯狂,但 1980 年代的程序员经常面临使用“C-ish”编译器或用汇编语言编写所有内容之间的选择。MISRA 中的一些规则,包括“固有类型”规则,旨在确保程序即使在被int视为 8 位类型的奇怪实现上运行也能正常工作。

于 2018-10-18T20:25:31.513 回答