无符号算术运算中的回绕

Posted

技术标签:

【中文标题】无符号算术运算中的回绕【英文标题】:Wraparound in unsigned arithmetic operation 【发布时间】:2016-04-04 12:37:04 【问题描述】:

分析以下代码时出现以下 MISRA 违规:

#define z  (USHORT)14745

#define Convert(x) ((((USHORT)x*(unsigned short)1000) + ((z) / (USHORT)2)) /(z))

static const USHORT array [2] = 


   Convert(176), -> Line "1"
   Convert(206)  -> Line "2"
;

在“1”、“2”两行都检测到以下 MISRA 违规:

积分提升:unsigned short 提升为 unsigned int。 参考 - ISO:C90-6.2.1.1 字符和整数

常量:。 MISRA-C:2004 规则 12.11;参考 - ISO:C90-6.1.2.5 类型

此强制转换的结果被隐式转换为另一种类型。

我的问题是:为什么在这个操作中会有一个环绕?!

注意:当我使用调试器检查array 的值时:

array [2] = 

  12,
  14
 

哪些是正确的值。

【问题讨论】:

因为176000206000都超过了USHORT可以容纳的值。 凌乱的“转换”行应该是什么?宏的一部分?据我所知,它不是有效的 C。 @Vane,但是最后的结果是正确的,为什么?! @Lundin,检查最后的编辑 规则 12.11 是建议性的。假设您在转换中“幸运”,并且有规则可以防止您“不幸”.. 【参考方案1】:

首先,176 * 1000 不适合 16 位无符号短整数。因此,通过使用 MISRA,您可以防止代码中出现严重错误,因为该算法是根据有符号的 int 类型计算的,并且它的结果隐含地显示为无符号短。如果你得到了预期的结果,那完全是巧合/运气。

请注意,还有另外两个未报告的建议性 MISRA 违规行为:

不允许使用类似函数的宏(规则 19.7) 您应该使用一组预定义的整数类型定义,例如 stint.h(规则 6.3)

这两个都是非常好的规则,不应该忽略。 (它也应该警告你使用不带 'u' 后缀的文字。)

解决方法是用类型安全函数替换混乱的宏,该函数不包含隐式提升(给定 32 位 int):

uint16_t convert (uint16_t x)

  const uint16_t z = 14745u;
  uint32_t result32;

  result32 = (uint32_t)x * 1000ul + (uint32_t)z / 2ul / (uint32_t)z
  return (uint16_t)result32;

【讨论】:

"因为算法是在有符号整数上计算的" 你的意思是 UNsigned int 吗? @ArmiaWagdy 不,所有小整数类型,例如 unsigned short 都会在表达式中使用时隐式提升为(有符号)int。这是微妙而危险的,也是为什么 MISRA 充满了非常完善的规则来防止此类错误。 MISRA 的主要任务之一实际上似乎是教程序员了解隐式晋升的危险。有很多资深的 C 程序员不知道 C 中的各种隐式类型提升规则。我承认,在 2004 年阅读 MISRA 之前,我自己并不知道。【参考方案2】:

与积分促销有关:

小于 int 的整数类型在操作时被提升 对他们进行。如果原始类型的所有值都可以 表示为 int,较小类型的值转换为 一个整数;否则,将其转换为无符号整数。

这样,Convert(206) [((((unsigned short)206*(unsigned short)1000) + ((z) / (USHORT)2)) /(z))] 宏将按如下方式执行:

    “206”将提升为带符号整数。 “1000”将提升为带符号整数。 将执行“206 * 1000”操作,结果也是signed int 类型 -> “206000”。 z, 2 将被提升为signed int。 将执行 z / 2 操作,结果将是有符号 int 类型 -> "14745 / 2" = "7372" 6."206000" + "7372" 会执行,结果是signed int -> 213372 “213372”将除以“14745”得到“14”

【讨论】:

从技术上讲,整数常量已经是int 类型。尽管您将其中一些投给了 USHORT,但这些将被提升回int。这意味着这个 sn-p 中的所有演员都是完全多余的。

以上是关于无符号算术运算中的回绕的主要内容,如果未能解决你的问题,请参考以下文章

关于 java中的算术运算符与逻辑运算符

算术运算

算术运算

算术运算

算术运算

Python3中的算术运算符