为什么(1>>> 0x80000000)== 1?

时间:2011-09-29 20:52:33

标签: c bit-manipulation

数字1,右移大于0,应为0,对吗?然而,我可以输入这个打印1的非常简单的程序。

#include <stdio.h>

int main()
{
        int b = 0x80000000;
        int a = 1 >> b;
        printf("%d\n", a);
}

在linux上使用gcc进行测试。

4 个答案:

答案 0 :(得分:30)

6.5.7按位移位运算符:

  

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

显然,编译器有权做任何事情,但最常见的行为是完全消除表达式(以及依赖于它的任何东西),或者简单地让底层硬件为它做任何事情。范围转移。许多硬件平台(包括x86和ARM)都会屏蔽一些低位,以用作移位量。实际的硬件指令将给出您在这两个平台上观察到的结果,因为移位量被屏蔽为零。因此,在您的情况下,编译器可能已经优化了移位,或者可能只是让硬件做它做的任何事情。如果你想知道哪个,请检查装配。

答案 1 :(得分:2)

根据标准,移位超过实际存在的位可能导致未定义的行为。所以我们不能责怪编译器。

动机可能存在于0x80000000的“边界含义”中,它位于最大正负一起的边界(并且是具有最高位设置的“负”)并且在某些检查上应该完成并且编译后的程序并不是为了避免浪费时间来验证“不可能”的东西(你真的希望处理器将位移30亿次吗?)。

答案 2 :(得分:1)

很可能试图移动一些大量的位。

您系统上的

INT_MAX 可能是 2**31-1,或0x7fffffff(我使用**来表示取幂)。如果是这种情况,那么在声明中:

int b = 0x80000000;

(问题中缺少分号;请复制并粘贴完全代码)常量0x80000000的类型为unsigned int,而非{{1 }}。该值隐式转换为int。由于结果超出了int的范围,结果是实现定义的(或者,在C99中,可能会引发实现定义的信号,但我不知道有任何实现这样做)。

最常见的方法是将无符号值的位重新解释为2的补码有符号值。在这种情况下,结果是int-2**31

因此,行为不是未定义的,因为您正在移动等于或超过类型-2147483648的宽度的值,它是未定义的,因为您正在移动(非常大)负数 value。

当然不重要; undefined未定义。

注意:以上假设您的系统int为32位。如果int宽于32位,那么大部分都不适用(但行为仍未定义)。

如果您真的想尝试按int位移位,可以这样做:

0x80000000

unsigned long b = 0x80000000; unsigned long a = 1 >> b; // *still* undefined 保证足以容纳值unsigned long,因此您可以避免部分问题。

当然,移位的行为与原始代码中的行为一样未定义,因为0x80000000大于或等于0x80000000的宽度。 (除非你的编译器有真的unsigned long类型,但没有真正的编译器那样做。)

避免未定义行为的唯一方法就是不要做你想要做的事情。

可能,但很可能,原始代码的行为未定义。只有当unsigned long0x80000000unsigned int的实现定义转换产生0到0范围内的值时,才会发生这种情况.31。IF int小于32位,转换可能会产生0。

答案 3 :(得分:0)

读好也许可以帮到你

表达式1&gt;&gt;表达式2

&gt;&gt; operator掩盖表达式2以避免过多地移动expression1。

因为如果移位量超过了expression1的数据类型中的位数,则所有原始位将被移走以给出平凡的结果。

现在确保每个班次至少留下一个原始位, 班次操作员使用以下公式计算实际班次金额:

使用一个小于expression1中的位数的掩码表达式2(使用按位AND运算符)。

实施例

var x : byte = 15;
// A byte stores 8 bits.
// The bits stored in x are 00001111
var y : byte = x >> 10;
// Actual shift is 10 & (8-1) = 2
// The bits stored in y are 00000011
// The value of y is 3
print(y); // Prints 3

“8-1”是因为x是8个字节所以操作将是7位。 void删除原始链位的最后一位