37

我刚刚检查了C++标准。似乎以下代码不应该是未定义的行为

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 具有带符号类型和负值,则结果值是实现定义的。

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

GCC 为代码片段提供以下警告,并且res67108863

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

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

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

4

2 回答 2

46

1Shift 运算符部分中的C++ 标准草案说(强调我的):5.8

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

因此,如果unsigned int32 bits或更少,那么这是未定义的,这正是gcc给你的警告。

于 2013-09-20T13:43:49.537 回答
19

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

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

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

于 2013-09-20T13:54:02.070 回答