Chinaunix首页 | 论坛 | 博客
  • 博客访问: 374180
  • 博文数量: 105
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 826
  • 用 户 组: 普通用户
  • 注册时间: 2013-02-16 13:58
个人简介

记录有意义的东西

文章分类

全部博文(105)

文章存档

2013年(105)

我的朋友

分类: C/C++

2013-04-24 09:05:53

http://www.cnblogs.com/jthou/articles/713740.html

对一个整形数右移32位会发生什么情况?

可以试试这段代码:

int main(int argc, char* argv[])

{

int i, j;

i = 0x0FFF;

j = i>>32;

return 0;

}

你会发现j仍然等于0x0FFF,而不是期望中的0。

在编译的时候,编译器会提示(在vc6和gcc4中都一样):“shift count is too large”

在这个程序中到底发生了什么事情呢?我们来看一看这段代码的汇编代码(Debug):

mov WORD PTR [ebp-8], 4095 ;11.3

$LN3:

movzx eax, WORD PTR [ebp-8] ;12.8

movsx eax, ax ;12.8

通过这段代码我们可以看到,编译器直接把代码编译成了赋值操作,而没有做移位操作.

再看一下Release版:

mov eax, 4095 ;11.2

这下更简单,直接给eax赋值了。

再看一下下面这个例子:

int main(int argc, char* argv[])

{

short i, j;

for(int k=0; k<=32; k++)

{

i = 0x0FFF;

j = i>>k;

printf("%d %d\n", i, j);

}

return 0;

}

这时候再看一下Release版的汇编代码:

.B1.2: ; Preds .B1.3 .B1.1

mov ecx, ebx ;12.11

mov eax, 4095 ;12.11

sar eax, cl ;12.11

movsx edx, ax ;12.11

push edx ;13.24

push 4095 ;13.24

push OFFSET FLAT: ??_C@_06A@?$CFd?5?$CFd?6?$AA@ ;13.24

call _printf ;13.4

; LOE ebx esi edi

.B1.7: ; Preds .B1.2

add esp, 12 ;13.4

; LOE ebx esi edi

.B1.3: ; Preds .B1.7

add ebx, 1 ;9.22

cmp ebx, 32 ;9.2

jle .B1.2 ; Prob 96% ;9.2

就会发现,编译器使用了sar指令。

如果把前面例子中的printf去掉,也就是:

int main(int argc, char* argv[])

{

short i, j;

for(int k=0; k<=32; k++)

{

i = 0x0FFF;

j = i>>k;

}

return 0;

}

这个时候,在gcc中不能编译,提示说有"unused variable",而在vc中可以顺利地编译,但我们看它生成的Rlease版汇编的时候就会发现,这两个变量运算的结果,编译器已经得知没有地方使用,就不再编译到binary code中了。

另外,我们会发现,不论在那种情况中,i>>32都等于i.

根据上述实验,我们可以得出这样的结论:

1.编译器能很好的找到程序中常量运算,然后用最简单的方式去替换它,比如前面的mov eax,4095

2.对于函数中的死代码,编译器也能很好的察觉,这些代码不会造成程序性能的下降和code size的增大。

3.这个应该是个常识,但很多C语言的书上都未提及,在C99中,对右移有这样的规定:

If the value of the right operand is negative or is

greater than or equal to the width of the promoted left operand, the behavior is undefined.

也就是说,对于右移大于或等于位宽的操作,或者右移负数的操作,其结果将依赖于编译器的处理和硬件指令的处理,并不唯一。

我们可以试试这个例子:

int main(int argc, char* argv[])

{

short i, j;

for(int k=-2; k<=35; k++)

{

i = 0x0FFF;

j = i>>(32-k);

printf("%d %d\n", i, j);

}

return 0;

}

X86上运行,当移负数位的时候,结果是0,当移大于等于32位的时候,结果同shift&31

对于上面的例子,比较保守的写法可以是:

int main(int argc, char* argv[])

{

int i, j;

for(int k=-1; k<=33; k++)

{

i = 0x0FFF;

int shift = 32-k;

j = shift<0?0:(shift>=32?0:(i>>shift));

}

return 0;

}

 

 

在 JVM 中,>> 操作,后面的数是 int 类型的。右移32位:

如果前一个操作数是 int 类型的话,取右移位数的低 5 位,相当于右移位数与 0x1f 做了 & 运算;
如果前一个操作数是 long 类型的话,取右移位数的低 6 位,相当于右移位数与 0x3f 做了 & 运算。

正如楼主的例子,>> 32,32(100000),可以看到它的低 5 位全是“0”,与 >> 0 的结果一致。

 

可见这取决于编译器是如何处理的。


阅读(1107) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~