c - 1U<<32 是 1,我是误解了 C 标准还是 GCC(Clang、MSVC)错误?

标签 c

1U << 32 有一些不同,首先将 32 分配给一个变量(例如,n)然后左移 n。我试过 printf("%u\n", 1U << 32) ,编译器会将结果优化为 0。但是当我尝试这段代码时,

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

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

根据C/C++标准,

The value of E1 << E2 is E1 left-shifted E2 bit positions; vacated bits are zero-filled. If E1 has an unsigned type, the value of the result is E1×2^E2, reduced modulo one more than the maximum value representable in the result type. Otherwise, if E1 has a signed type and non-negative value, and E1×2^E2 is representable in the corresponding unsigned type of the result type, then that value, converted to the result type, is the resulting value; otherwise, the behavior is undefined.

在我看来,当 E1 是无符号的并且 E1 << E2 溢出时,结果将是 E1 * 2 ^ E2 mod (UINT_MAX + 1),所以 1U << n 的结果应该是 0。

但是在 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 的描述,

The destination operand can be a register or a memory location. The count operand can be an immediate value or register CL. The count is masked to 5 bits, which limits the count range to 0 to 31.

ARM 的汇编程序指南告诉我们 lsl允许的类次是 0-31。

至少表示指令shl运行良好,这就是为什么我怀疑编译器的实现有误的原因,但似乎不可能出现如此广泛和长期的错误,有人可以向我解释一下吗?

谢谢。

最佳答案

The operands shall be of integral or unscoped enumeration type and integral promotions are performed. The type of the result is that of the promoted left operand. The behavior is undefined if the right operand is negative, or greater than or equal to the length in bits of the promoted left operand.

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

The integer promotions are performed on each of the operands. The type of the result is that of the promoted left operand. If the value of the right operand is negative or is greater than or equal to the width of the promoted left operand, the behavior is undefined.

C99,§6.5.7 ¶3(添加了强调)

这些规则的制定极有可能正是因为现有平台上的轮类运算符(operator)存在这种限制;如果没有此规则,未知值的每次移位都必须转换为比简单的底层平台移位指令更复杂的代码,而移位的操作速度非常快。

关于c - 1U<<32 是 1,我是误解了 C 标准还是 GCC(Clang、MSVC)错误?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48489700/

相关文章:

c - 使用 scanf() 函数进行 C 编程时出现意外输出

c - 如何在运行时为结构数组赋值?

c - (C) 使用月份中的天数和月份开始日显示日历(使用 1 表示星期一,2 表示星期二等)

c - C 中 replaceAll 中的内存泄漏

c - 为什么在将 C 向下转换从 unsigned int 转换为 unsigned char 时,movl 比 movb 更受欢迎?

c - 如果 (n!=0) n=0; v/s n=0;哪个更有效,为什么?

CS50 Pset3 函数 "Won"

c - 鉴于字节内存地址不是 int 类型,int 类型的 C 指针如何保存内存地址?

c - 'struct structVarable' 类型的空指针内的成员访问

c - 从文件中读取值