编程中发现的小问题:逻辑右移?算术右移?

今天在写代码的时候,遇到了一点小问题,代码如下。

int clearbits(unsigned long x, unsigned short p, unsigned short n)
{ 
	unsigned long high =  (~0) << (32 - p + 1);
	unsigned long low = (~0)>> (p + n - 1);
	unsigned long griddle = ~0 & (high | low);
	printf("griddle = 0x%08x, low = 0x%08x, high = 0x%08x\n", griddle, low, high);
	return x & griddle;
}
int main(int argc, char **argv)
{
    unsigned long x = 0xABCD;
    printf("0x%08x\n", clearbits(x, 5, 5));
    return 0;
}
函数的意思是对x的某些位数进行清除,但是实际运行的时候,结果与预期很不一样,在代码里加了打印进行调试,函数调用时,有如下打印。

griddle = 0xffffffff, low = 0xffffffff, high = 0xf0000000

low的值并非我想要的,即数值0xffffffff右移32 - p - n + 1位,并且高位补零,那么问题出在哪里呢?只好在反汇编下一看究竟。

    unsigned long low = (~0) >> (p + n - 1);
000C3D48  movzx       eax,word ptr [p]  
000C3D4C  movzx       ecx,word ptr [n]  
000C3D50  lea         ecx,[eax+ecx-1]  
000C3D54  or          edx,0FFFFFFFFh  
000C3D57  sar         edx,cl  
000C3D59  mov         dword ptr [low],edx
反汇编下,发现右移操作使用的是000C3D57  sar         edx,cl  ,即进行了算术右移,而并非逻辑右移,因此按照有符号数,将高位补1,而不是补0,这样就与我的预期不符了。

对代码进行如下修改

int clearbits(unsigned long x, unsigned short p, unsigned short n)
{ 
	unsigned long high =  (~0) << (32 - p + 1);
	unsigned long low = (unsigned long)(~0)>> (p + n - 1);
	unsigned long griddle = ~0 & (high | low);
	printf("griddle = 0x%08x, low = 0x%08x, high = 0x%08x\n", griddle, low, high);
	return x & griddle;
}
这样,继续运行,得到的结果就和预期相符了。

griddle = 0xf03fffff, low = 0x003fffff, high = 0xf0000000

反汇编再确认一下,使用的是000C3D43  shr         edx,cl ,即进行了逻辑右移,这样高位会补0,结果就和我的预期是相符的了。

	unsigned long low = (unsigned long)(~0) >> (p + n - 1);
000C3D34  movzx       eax,word ptr [p]  
000C3D38  movzx       ecx,word ptr [n]  
000C3D3C  lea         ecx,[eax+ecx-1]  
000C3D40  or          edx,0FFFFFFFFh  
000C3D43  shr         edx,cl  
000C3D45  mov         dword ptr [low],edx  

由这个问题可以看出,如果代码写成unsigned long low = (~0) >> (p + n - 1); 编译器会先认为对0取反后是一个有符号数,继而对其进行了算术右移操作。那么,如果我们不使用~0,而直接使用0xFFFFFFFF呢,即unsigned long low = (0xFFFFFFFF) >> (p + n - 1);,经过测试,这样会认为0xFFFFFFFF是一个无符号数,并随后进行逻辑右移操作。

这里,为了安全,涉及到右移操作时,还是需要对操作数的类型进行考量。




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值