2011-09-29 107 views
14

数字1右移了大于0的任何东西,应该是0,对吗?然而,我可以输入在这个非常简单的程序,它打印1.为什么(1 >> 0x80000000)== 1?

#include <stdio.h> 

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

测试Linux上的GCC。

+27

从技术上讲,它是未定义的行为,因为你的移动超过了类型的宽度(gcc应该在警告中报告)。 –

+2

除非你施放,否则不会要求它移动-1? – Marvo

+3

此外,“gcc”似乎完全尊重UB的精神,根据条件给出不同的“错误”结果(http://gcc.gnu.org/ml/gcc/2004-11/msg01131.html) –

回答

30

6.5.7按位的移位运算符:

如果右操作数的值是负的或大于或等于所述推动左操作数的宽度,该行为是未定义。

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

+0

@MooingDuck:你无法从观察到的行为中推断出这一点。还有其他几种可能的解释。 –

+0

我听说有些编译器通过使用RHS的5个最右边的位来实现这种行为,这解释了OP的行为。 –

+0

@StevemCanon:你说得对。这是纯粹的猜测。 –

2

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

该动机可能驻留在0x80000000的“边界含义”中,该坐标位于最大正数和负数的边界上(也就是具有最高位设置的“负数”),并且在某些应该完成的检查上编译的程序不会避免浪费时间来验证“不可能的”事情(你真的希望处理器转换位数30亿次吗?)。

+2

不是“可以导致”,“不导致”。 –

+0

@R ..:取决于你所处的位置:通过语言表明它的“结果”(在语言中没有任何关于它的意义)编译器设计者可以“定义”它的意义) –

1

这很可能是而不是试图移动一些大量的位。

INT_MAX您的系统上是可能2**31-1,或0x7fffffff(我使用**表示幂)。如果是这样的话,那么在声明:

int b = 0x80000000; 

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

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

所以行为不是不确定,因为你被等于或超过int类型的宽度值的转移,它是不确定的,因为你是一个(很大)偏移值。

当然,这不重要;未定义是未定义的。

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

如果你真的想尝试0x80000000位转移,你可以做这样的:使你避免这个问题的一部分

unsigned long b = 0x80000000; 
unsigned long a = 1 >> b; // *still* undefined 

unsigned long保证是大到足以容纳值0x80000000, 。

当然,由于0x80000000大于或等于unsigned long的宽度,因此移位的行为与原始代码中的行为一样。 (除非你的编译器有一个真的unsigned long型,但没有真正的世界的编译器做到这一点。)

,以避免不确定的行为的唯一方法就是不要做你想要做的事。

这是可能的 ,但几乎不可能, 您的原始代码的行为不是未定义的。如果从unsigned intint0x80000000实现定义转换产生在范围0的值只能发生。31. IF int小于32位,转换是可能产生0

+0

最后一段并非如此不太可能 - 它很可能在具有16位“int”的系统上,这远非闻所未闻。 – caf

+0

@caf:很对,我应该想到这一点。 –

0

以及读取也许可以帮助你

表达式>>表达式2

>>运算符口罩表达式2,以避免过多的转移表达式。

这是因为如果移位量超过了expression1的数据类型中的位数,所有的原始位将被移开以给出一个微不足道的结果。

现在对于确保每个移离开原来位中的至少一个, 移位运算符使用下面的公式来计算实际移位量:(使用逐位AND运算)与一种以下

掩模表达式2比expression1中的位数少。

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个字节故operacion将与7位。无效删除最后一位原始链位

相关问题