对一个整形数右移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;

}

 

(前天晚上帮同事debug的时候遇到移32位的问题,后引发了同事对C语言,编译器和硬件指令的一些讨论,遂做了点试验,写成此文)

posted @ 2007-04-15 01:18  老猴的技术博客  阅读(7365)  评论(2编辑  收藏  举报