【问题标题】:Sign extension on literal vs. variable文字与变量的符号扩展
【发布时间】:2011-10-25 08:19:58
【问题描述】:

我正在使用 gcc 4.4.5,在理解简单的无符号值上的右移运算符时遇到了一些困难...

这个测试

    ASSERT_EQ( 0u, (unsigned long)(0xffffffff) >> (4*8) );

通过。

这个测试

    unsigned long address = 0xffffffff;
    ASSERT_EQ( 0u, address >> (4*8) );

失败:

Value of: address >> (4*8)
   Actual: 4294967295
   Expected: 0u

似乎变量被视为有符号值,因此导致符号扩展。 (0xffffffff 十进制为 4294967295)。 谁能看出区别?

【问题讨论】:

  • ASSERT_EQ 是什么?它可能是进行促销或其他一些讨厌的事情。 ideone.com/WxyUc
  • @R.MartinhoFernandes:它来自 google gtest 框架。
  • 强制转换为无符号和右移都发生在被传递到ASSERT_EQ 之前,但我想它可以定义为ASSERT_EQ(x) foo((int)x) 或等效...
  • 根据优化级别,一个可能的区别是 (unsigned long)(0xffffffff) >> (4*8) 已被编译器评估为 0 作为编译时常量表达式,而 address >> (4*8) 已被不同地翻译,可能是基于相当于x >> y -> x >> (y & 0x1F) 的规则(我以前见过这种效果,不记得在哪里,与带有 5 位立即操作数的移位指令有关),或者可能通过几个步骤它的 UB-ness 导致它被完全删除。无论哪种方式,它都没有效果,留下实际值 4294967295。
  • 我的意思是,它不需要被奇怪的符号扩展,它只需要没有转移。

标签: c++ bit-shift


【解决方案1】:

移动大于或等于左操作数位大小的值是未定义的行为(第 5.8 节¶1)。 (我假设unsigned long 是您的 cmets 的 32 位,如果您考虑符号扩展,则 0xfffffff 是预期的结果。)

不过,ASSERT_EQ 可能会导致差异,因为它在 GCC 4.5 with good old assert 上运行良好。

【讨论】:

  • 发送!我才得出这个结论。而且...编译器警告我。一个过失已经到位:)
  • “如果右操作数为负数,或者大于或等于提升的左操作数的位长度,则行为未定义”哇,太震惊了!我看不出有什么好的理由——原始机器指令完美地定义了过度移位。
  • @spraff:像往常一样,可能有一些架构没有定义这些转变......
  • @R.MartinhoFernandes:以及其他定义不同的处理器。不定义此类移位允许编译器只使用原始移位指令而不必生成特殊情况代码以防正确的操作数是奇怪的。
【解决方案2】:

我认为这完全取决于未定义的行为。我相信,如果右操作数大于或等于左操作数中的位数,则按位移位的结果是不确定的。

【讨论】:

    【解决方案3】:

    如果unsigned long 是 32 位,则将其移动 32 位的行为是未定义的。引用 C++ 2003 标准:

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

    显然,编译时和运行时的评估是不同的——这是完全有效的,只要它们在定义的情况下产生相同的结果。

    (如果您的系统上unsigned long 的宽度超过 32 位,则不适用。)

    【讨论】:

      【解决方案4】:

      这两个测试pass on gcc-4.3.4 和普通的旧assert

      【讨论】:

      • gcc-4.3.4 也给出警告吗?
      • 是的,请参阅链接中的“编译信息”。
      猜你喜欢
      • 2012-06-29
      • 1970-01-01
      • 1970-01-01
      • 2016-08-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多