数字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。
数字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。
6.5.7按位的移位运算符:
如果右操作数的值是负的或大于或等于所述推动左操作数的宽度,该行为是未定义。
显然,编译器有权执行任何操作,但最常见的行为是优化表达式(以及任何依赖于它的内容),或者直接让底层硬件执行任何操作范围转变。许多硬件平台(包括x86和ARM)都会屏蔽一些低位来作为移位量。实际的硬件指令将给出您在这些平台上观察到的结果,因为移位量被屏蔽为零。所以在你的情况下,编译器可能已经优化了这种转换,或者它可能只是让硬件做它所做的任何事情。如果你想知道哪一个,请检查装配。
@MooingDuck:你无法从观察到的行为中推断出这一点。还有其他几种可能的解释。 –
我听说有些编译器通过使用RHS的5个最右边的位来实现这种行为,这解释了OP的行为。 –
@StevemCanon:你说得对。这是纯粹的猜测。 –
根据标准,移位超过实际存在的位可能会导致未定义的行为。所以我们不能责怪编译器。
该动机可能驻留在0x80000000的“边界含义”中,该坐标位于最大正数和负数的边界上(也就是具有最高位设置的“负数”),并且在某些应该完成的检查上编译的程序不会避免浪费时间来验证“不可能的”事情(你真的希望处理器转换位数30亿次吗?)。
不是“可以导致”,“不导致”。 –
@R ..:取决于你所处的位置:通过语言表明它的“结果”(在语言中没有任何关于它的意义)编译器设计者可以“定义”它的意义) –
这很可能是而不是试图移动一些大量的位。
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 int
到int
的0x80000000
实现定义转换产生在范围0的值只能发生。31. IF int
小于32位,转换是可能产生0
最后一段并非如此不太可能 - 它很可能在具有16位“int”的系统上,这远非闻所未闻。 – caf
@caf:很对,我应该想到这一点。 –
以及读取也许可以帮助你
表达式>>表达式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位。无效删除最后一位原始链位
从技术上讲,它是未定义的行为,因为你的移动超过了类型的宽度(gcc应该在警告中报告)。 –
除非你施放,否则不会要求它移动-1? – Marvo
此外,“gcc”似乎完全尊重UB的精神,根据条件给出不同的“错误”结果(http://gcc.gnu.org/ml/gcc/2004-11/msg01131.html) –