【问题标题】:1U<<32 is 1, am I misunderstood C standard or GCC (Clang, MSVC) buggy?1U<<32 是 1,我误解了 C 标准还是 GCC(Clang,MSVC)错误?
【发布时间】:2018-01-29 03:03:49
【问题描述】:

1U printf("%u\n", 1U << 32),编译器会将结果优化为0。但是当我尝试这段代码时,

#include <stdio.h>
int main() {
    int n = 32;
    printf("%u\n", 1U << n);
}

编译执行上面的代码会打印出1的结果。

根据C/C++标准,

E1的值

在我看来,当E1无符号并且E1

但是在 x86 和 ARM 上使用 GCC 4.4 到 7.2 或 Clang 3.1 到 5.0 编译结果为 1。我检查了汇编代码,发现两者都产生了以下汇编,

movl   $20,-0x4(%rbp)
mov    -0x4(%rbp),%eax
mov    $0x1,%edx
mov    %eax,%ecx
shl    %cl,%edx

orr     w8, wzr, #0x1
orr     w9, wzr, #0x20
stur    w9, [x29, #-4]
ldur    w9, [x29, #-4]
lsl     w1, w8, w9

然后我检查了shllsl 的指令,在c9x.me 上我发现以下关于shl 的描述,

目标操作数可以是寄存器或内存位置。计数操作数可以是立即数或寄存器 CL。计数被屏蔽为 5 位,这将计数范围限制为 0 到 31。

ARM 的汇编程序指南告诉lsl 允许的移位是 0-31。

这意味着至少指令shl运行良好,这就是为什么我怀疑编译器的实现是错误的,但似乎不可能有这么长时间的这样一个错误,谁能给我解释一下?

谢谢。

【问题讨论】:

  • 首先,对于语义规则,您应该只指定一种语言。 C 和 C++ 是两种非常不同的语言。他们的标准也非常不同,即使是类似的事情也有不同的措辞。
  • "ARM 的汇编指南告诉我们,lsl 允许的移位是 0-31",而"计数被屏蔽为 5 位"意味着你的 32 变成了 0,所以不执行移位。
  • &lt;stdio&gt; 既不是 C 也不是 C++。可能是我一直听说的这种新的“C/C++”语言。
  • 但是您确实引用了“C/C++ 标准”。找到这份超级机密文件了吗?

标签: c


【解决方案1】:

操作数应为整数或非范围枚举类型,并执行整数提升。结果的类型是提升的左操作数的类型。 如果右操作数为负数、或大于或等于提升的左操作数的位长度,则行为未定义

C++11,§5.8 ¶1(强调)

对每个操作数执行整数提升。结果的类型是提升的左操作数的类型。如果右操作数的值为负数或大于或等于提升的左操作数的宽度,则行为未定义

C99,§6.5.7 ¶3(强调)

这些规则极有可能是因为现有平台上的班次运营商存在这种限制而制定的;如果没有这个规则,未知值的每个移位都必须转换为比简单的底层平台移位指令更复杂的代码,而移位操作预计会非常快。

【讨论】:

    【解决方案2】:

    注意:目标操作数可以是寄存器或内存位置。计数操作数可以是立即数或寄存器 CL。计数被屏蔽为 5 位,这将计数范围限制为 0 到 31。

    值 32 是:0x00000020

    限制为 5 位为:0x00000000

    1

    【讨论】:

      猜你喜欢
      • 2017-10-17
      • 1970-01-01
      • 2018-01-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-05-21
      • 1970-01-01
      • 2011-01-11
      相关资源
      最近更新 更多