参考自:https://www.cnblogs.com/jthou/articles/713740.html
用64位编译器可以试试下面这段代码:
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”,或者“right shift count >= width of type”(x86-64)。
在这个程序中到底发生了什么事情呢?我们来看一看这段代码的汇编代码(Debug):
mov WORD PTR [ebp-8], 4095 ;11.3
$LN3:
movzx eax, WORD PTR [ebp-8] ;12.8
movsx eax, ax ;12.8
通过这段代码我们可以看到,编译器并未使用直接把代码编译成了赋值操作,而没有做移位操作(sar)。
再看一下Release版:
mov eax, 4095 ;11.2
这下更简单,直接给eax赋值了。
另外,我们会发现,64位编译器不论在哪种情况中,i>>32 都等于i,i<<32 都等于i。
右移操作 `在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. 也就是说,对于右移大于或等于位宽的操作,或者右移负数的操作,其结果将依赖于编译器的处理和硬件指令的处理,并不唯一。
我们可以试试这个例子:
#include <stdio.h>
int main(int argc, char* argv[])
{
int i, j, k;
for(int n=-2; n<=35; n++)
{
i = 0x0FFF;
j = i>>(32-n);
k = i>>32;
printf("shift:%2d, j: 0x%x\n", (32-n), j);
}
return 0;
}
在X86上运行,当移负数位的时候,结果是0,当移大于等于32位的时候,结果同shift&31。 执行结果如下:
另外,在riscv64位和arm32位上也对该测试用例进行了验证,结果如下(左图为riscv64,右图为arm32):
对于上面的例子,比较保守的写法可以是:
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;
}
|