左移右移运算符的探究

本文转载于:hgl868(哈哈,谢谢啦!!!):http://blog.csdn.net/hgl868/article/details/7058909



关于逻辑移位、算术移位可参见迅雷深大笔试题部分。的一道题。

以前看到C++标准上说,移位运算符(<<、>>)出界时的行为并不确定:

The behavior is undefined if the right operand is negative, orgreater than or equal to the length in bits of the promoted left operand.

我当时也没有深究过这个问题。前几天有个网友来信问起这件事,我才发现,这和IntelCPU的移位运算有关。下面是那位网友的来信以及我的回复:

 

您好!运算符<<作为位操作中的高效的操作,但我遇到一个问题:下面在VC环境下发现一个很不明白的地方,下面标注。

#include <stdio.h>

void main()

{

   unsigned int i,j;

   i=35;

 

   //为什么下面两个左移操作结果不一样?

   j=1<<i;  // j为8

   j=1<<35; // j为0

}

不知是哪里没有理解对。

 

原因是这样的:i=35;j=1<<i;这两句在VC没有做优化的情况下,将被编译成下面的机器指令:

mov dword ptr [i],23h

mov eax,1

mov ecx,dword ptr [i]

shl eax,cl

mov dword ptr [j],eax

在shl一句中,eax=1,cl=35。而IntelCPU执行shl指令时,会先将cl与31进行and操作,以限制左移的次数小于等于31。因为35 & 31 =3,所以这样的指令相当于将1左移3位,结果是8。

而j=1<<35;一句是常数运算,VC即使不做优化,编译器也会直接计算1<<35的结果。VC编译器发现35大于31时,就会直接将结果设置为0。这行代码编译产生的机器指令是:

mov dword ptr [j],0

对上面这两种情况,如果把VC编译器的优化开关打开(比如编译成Release版本),编译器都会直接将结果设置为0。

所以,在C/C++语言中,移位操作不要超过界限,否则,结果是不可预期的。

下面是Intel文档中关于shl指令限制移位次数的说明:

The destination operand can be a register or a memory location.The count operand can be an immediate value or register CL. The count is maskedto 5 bits, which limits the count range to 0 to 31. A special opcode encodingis provided for a count of 1.



将1变为0,将0变为1

EG:

~(10011010)

(01100101)

注:

VC++编译器,计算~10,得出的结果是-11。为什么不是5呢

10的二进制表示为1010,按位取反应该为0101,也就是十进制的5,为什么会得出-11?

VC是32位编译器,所以

10 = 00000000 00000000 00000000   00001010

~10 = 11111111 11111111   11111111   11110101 =   -11


右移:

a>>b(将对象a右移b位)

右移时:移出的位就会被直接丢弃,而且a的值不会改变

对于有符号的数,如:long,int, short,char,右移时,符号位(最高位)一起移动,并且大多数C/C++编译器规定,如果原符号位最高位为1,那么右移时,最高位就补充1,原符号位为0,那么右移时,最高位就补0


#include <stdio.h>
void main()
{
	int i,m;
	unsigned j,n;
	i = 10; 
	j = 10;
	m = -10;
	n = -10;

	printf("i is :%d\n,j is :%d \n",i>>3, j>>3 );
	printf("m is :%d\n,n is :%d n is :%0x\n",m>>3, n>>3, n>>3);
}

运行结果:

左移右移运算符的探究_第1张图片


由上可知:

int类型下(32位):   10的存储情况:                              00000000   00000000   00000000   00001010

unsinged类型下(默认是unsigned int):10的存储情况:00000000   00000000   00000000   00001010

当右移3位之后都变成了(高位补0):                               00000000   00000000   00000000   00000001  


int类型的-10,(32位下),存储情况为:                         

                                     11111111  11111111  11111111  11110110

故:右移3位之后为:   11111111  11111111  11111111  11111110

如上,也就是-2的补码


unsigned类型的-10,对于这种情况,在存储的情况下,应该是当作没有符号位的,将最高位也算进去了:

所以-10的存储应该为:                                                            11111111  11111111  11111111  11110110(fffffff6)

所以右移3位之后(前面补的应该是0,因为是无符号的)故: 00011111  11111111  11111111  11111110(1ffffffe)

以十进制表示出来也就是上面的:536870910

你可能感兴趣的:(左移右移运算符的探究)