- 可以试试这段代码:
- 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 的结果一致。
-
- 可见这取决于编译器是如何处理的。
复制代码 |