【问题标题】:Is right shift undefined behavior if the count is larger than the width of the type?如果计数大于类型的宽度,是否右移未定义行为?
【发布时间】:2013-09-25 22:18:33
【问题描述】:

我刚刚检查了 C++ 标准。看来下面的代码不应该是undefined behavior:

unsigned int val = 0x0FFFFFFF;
unsigned int res = val >> 34;  // res should be 0 by C++ standard,
                               // but GCC gives warning and res is 67108863

从标准来看:

E1 >> E2 的值是 E1 右移 E2 位的位置。 如果 E1 有一个无符号类型,或者如果 E1 有一个有符号类型和一个非负数 值,结果的值是商的整数部分 E1/2^E2. 如果 E1 具有带符号类型和负值,则结果 value 是实现定义的。

根据标准,由于 34 不是负数,所以变量 res 将为 0。

GCC 对代码 sn-p 给出以下警告,res67108863

警告:右移计数 >= 类型宽度

我还检查了 GCC 发出的汇编代码。它只是调用SHRL,而英特尔的 SHRL 指令文档中,res 不是零。

那么这是否意味着 GCC 没有在英特尔平台上实现标准行为?

【问题讨论】:

  • 你给出的标准引用是关于第一个操作数的,即val,而不是34
  • @ChristianRau,你是对的。

标签: c++ gcc assembly standards undefined-behavior


【解决方案1】:

5.8 部分中的draft C++ standard 1 段中的 Shift 运算符 说(强调我的):

结果的类型是提升的左操作数的类型。 如果右操作数为负数,或大于或等于提升的左操作数的位长度,则行为未定义

因此,如果 unsigned int32 bits 或更小,则这是未定义的,这正是 gcc 给您的警告。

【讨论】:

  • @Zaibis:是的,GCC 能够编译 C++。 GCC 是(仅)一个 C 编译器,或者 G++ 是一个不同的编译器,这是一个常见的误解。
  • @Zaibis:GCC 是一个工具链;它有 C 和 C++ 的编译器(和链接器和许多其他东西)。
【解决方案2】:

要准确解释会发生什么: 编译器会将34 加载到一个寄存器中,然后将您的常量加载到另一个寄存器中,并对这两个寄存器执行右移操作。 x86 处理器对移位值执行“shiftcount % bits”,这意味着您将右移 2。

由于 0x0FFFFFFF(十进制 268435455)除以 4 = 67108863,这就是您看到的结果。

如果您有不同的处理器,例如 PowerPC(我认为),它可能会给您零。

【讨论】:

  • 如果我错了,请纠正我,但最近几天我在 PPC 和移位方面做了很多工作,而 gcc 无法在 PPC32 上表示整数的 32 位移位。那么你确定你的陈述提到 PPC 吗? (我确实使用 C 源代码,但如果 CPU 架构无法这样做,那么该语言不应该是相关的,应该吗?)
  • @Zaibis GCC 是一个可移植的编译器,没有理由禁用 GCC 内部的代码,该代码会在为 PowerPC 发出代码时警告移位过大。但我可以向你保证,PowerPC 处理器确实有几个额外的晶体管,它们允许右移的汇编指令在移位 34 时产生预期的结果。
猜你喜欢
  • 1970-01-01
  • 2022-08-04
  • 2015-11-11
  • 2014-06-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多