一个C/C++中的移位操作问题

14 篇文章 0 订阅
12 篇文章 1 订阅

摘要:关于c/c++中由于CPU位宽造成的一个移位操作问题,b = ~a>>1中b的值的问题。

先看一段代码:

 1:  #include <stdio.h>
 2:  
 3:  int main()
 4:  {
 5:      unsigned char a = 0xAA ;
 6:      unsigned char b = 0x00 ;
 7:      b = ~a>>1 ;
 8:      printf("0x%X",b) ;
 9:      return 0 ;
10:  }

VC中运行的输出结果是:0xAA,原书中解释是运算符~的优先级小于>>(当然这是不可能滴,毫无疑问~优先于>>),但是最后的结果感觉确实是运算>>再做~操作的。但是我个人觉得应该是0x2A,于是又在TC下运行,结果果然是0x2A,与自己的计算结果相同。

为什么会有不同结果呢?于是我在其他多种编译器上运行了这段代码,比如:gcc,lcc,bcc等,结果都是0xAA,那么问题又在哪儿呢?

即使将程序代码修改为:

1:  b = (~a)>>1 ;

结果还是一样,说明确实是~运算符优先于>>。程序也是这样执行的。

最后我看了一下VC中的汇编代码:

1:  0041339E  mov         byte ptr [a],0AAh 
2:  004133A2  mov         byte ptr [b],0 
3:  
4:  004133A6  movzx       eax,byte ptr [a] 
5:  004133AA  not         eax  
6:  004133AC  sar         eax,1 
7:  004133AE  mov         byte ptr [b],al 

终于弄懂了是怎么回事,在对a取反的时候先进行了一个自动的类型转换,转换结果为int型(从这里可以看出32位机CPU一次必须操作32个bit,不能只操作其中低8位),所以~a运算的结果就是0xFFFFFF55,那么对于有符号的数做右移操作实际上用的是SAR指令(不是SHR,两者的区别在后面说明),移位之后结果变为0xFFFFFFAA,然后在将其转换为unsigned char 类型赋值给变量b,那么b的值最后就是0xAA了。

那么为什么在TC上的结果会是0x2A了呢?那是因为TC中模拟的是8086体系的CPU结构(一个验证方法就是sizeof(int)==2),相当于运行于16bit位宽的计算机上,在这种结构中,CPU的寄存器位宽是16bit,但是有部分16bit寄存器可以分为两个8bit寄存器来独立操作,也就是说,在TC上运行时,~a操作不会进行类型转换,并且由于是unsigned char类型,结果就是0x55,然后直接对8位宽的0x55右移(采用SHR指令),结果就是0x2A了。

这个问题可以简化为:

1:  unsigned int c = 0xFFAA;
2:  int b  ;
3:  unsigned int d;
4:  b = a>>1 ;  /* 0xFFD5  */
5:  d = c>>1 ;  /* 0x7FD5  */

可以使用Keil验证上述想法,在Keil C166中采用16为的CPU和Keil for Arm中采用32位的CPU结果与上述描述的一致。

补充知识

;SHR(Shift Right):      逻辑右移    最高位移动至次高位,然后最高位补零
;SAR(Shift Arithmetic Right): 算术右移 这种情况下,最高位是符号位,最高位移动至次高位,最高位保持不变
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值