在有符号整数上进行算术位移

我想弄清楚算术位移运算符在C中的工作方式,以及它将如何影响带符号的32位整数。

为了简单起见,假设我们在一个字节(8位)内工作:

x = 1101.0101 MSB[ 1101.0101 ]LSB 

阅读堆栈溢出和一些网站上的其他post,我发现: <<将转向MSB(在我的情况左边),并用0填充“空”LSB位。

而且>>将移向LSB(在我的情况下是右边的),并用MS位填充“空”位

因此, x = x << 7将导致将LSB移到MSB,并将所有内容都设置为0。

 1000.0000 

现在,让我们说,我会>> 7 ,最后的结果。 这会导致[0000.0010] ? 我对吗?

关于换class经营者的假设我是否正确?

我刚在我的机器上testing过,**

 int x = 1; //000000000......01 x = x << 31; //100000000......00 x = x >> 31; //111111111......11 (Everything is filled with 1s !!!!!) 

为什么?

负号的右移有实现定义的行为。

如果你的8位代表一个有符号的8位值(正如你在谈到一个“有符号的32位整数”,然后切换到8位的例子),那么你有一个负数。 向右移动可以用原始的MSB填充“空”位(即,执行符号扩展),或者可以根据平台和/或编译器移入零。

(实现定义的行为意味着编译器会做一些明智的事情,但是是以一种依赖于平台的方式;编译器文档应该告诉你什么)。


左移,如果数字开始为负数,或者移位操作会将1移到或超出符号位,则会有未定义的行为(如导致溢出的带符号值的大多数操作)。

(未定义的行为意味着什么都可能发生。)


在两种情况下,对无符号值的操作都是相同的:“空”位将被填充0。

按位移动操作未定义为负值

为“<<”

6.5.7 / 4 […]如果E1具有带符号的types和非负值,并且E1× E2 E2可以在结果types中表示,那么这是结果值; 否则,行为是不确定的。

和“>>”

6.5.7 / 5 […]如果E1有签名types和负值,则结果值是实现定义的。

研究这些操作在特定实现的签名数字上的行为是浪费时间的,因为您不能保证它在任何其他实现上的工作方式都是相同的(例如,您的计算机上的编译器具体的commad-line参数)。

它甚至可能不适用于相同编译器的较旧版本或较新版本。 编译器甚至可能将这些位定义为随机或未定义的。 这就意味着,在源代码中使用完全相同的代码序列可能会产生完全不同的结果,甚至会依赖于程序集优化或其他寄存器使用情况。 如果封装在函数中,它可能甚至不会在具有相同参数的两次连续调用中的那些位上产生相同的结果。

仅考虑非负值 ,左移1( expression << 1 )的效果与将expression式乘以2(提供的expression式* 2不溢出)以及右移1( expression >> 1 )除以2相同。

正如其他人所说,负面价值的转移是实施定义的。

大多数实现通过使用符号位填充移位位来将签署的右移作为floor(x / 2N )处理。 实践中非常方便,因为这个操作非常普遍。 另一方面,如果你将右移无符号整数,位移位将被清零。

从机器端来看,大多数实现都有两种types的右移指令:

  1. 一个“算术”右移(通常具有助记符ASR或SRA),正如我所解释的那样。

  2. 一个“逻辑”右移(有助记符LSR或SRL或SR),正如你期望的那样工作。

大多数编译器首先使用签名types,其次使用未签名types。 只是为了方便。

在32位编译器中

x = x >> 31;

这里x是有符号整数,所以第32位是符号位。

最终的x值是100000 … 000 。 第32位表示有效值。

这里x值实现为1的恭维。

那么最后的x是-32768