您的位置:首页 > 编程语言 > C语言/C++

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

2010-08-04 14:17 253 查看
摘要:关于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:  }


.codearea{ color:black; background-color:white; line-height:18px; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys","BitStream Vera Sans Mono", courier,monospace,serif}
.codearea pre{ color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}
.linewrap pre{white-space:pre-wrap; white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word; word-break:normal}
.codearea pre.alt{ background-color:#f7f7ff !important}
.codearea .lnum{color:#4f81bd;line-height:18px}

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

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

即使将程序代码修改为:

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


.codearea{ color:black; background-color:white; line-height:18px; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys","BitStream Vera Sans Mono", courier,monospace,serif}
.codearea pre{ color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}
.linewrap pre{white-space:pre-wrap; white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word; word-break:normal}
.codearea pre.alt{ background-color:#f7f7ff !important}
.codearea .lnum{color:#4f81bd;line-height:18px}

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

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

1:  0041339E  mov         byte ptr [a],0AAh


2:  004133A2  mov         byte ptr ,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


.codearea{ color:black; background-color:white; line-height:18px; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys","BitStream Vera Sans Mono", courier,monospace,serif}
.codearea pre{ color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}
.linewrap pre{white-space:pre-wrap; white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word; word-break:normal}
.codearea pre.alt{ background-color:#f7f7ff !important}
.codearea .lnum{color:#4f81bd;line-height:18px}

终于弄懂了是怎么回事,在对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  */


.codearea{ color:black; background-color:white; line-height:18px; border:1px solid #4f81bd; margin:0; width:auto !important; width:100%; overflow:auto; text-align:left; font-size:12px; font-family: "Courier New","Consolas","Fixedsys","BitStream Vera Sans Mono", courier,monospace,serif}
.codearea pre{ color:black; line-height:18px; padding:0 0 0 12px !important; margin:0em; background-color:#fff !important}
.linewrap pre{white-space:pre-wrap; white-space:-moz-pre-wrap; white-space:-pre-wrap; white-space:-o-pre-wrap; word-wrap:break-word; word-break:normal}
.codearea pre.alt{ background-color:#f7f7ff !important}
.codearea .lnum{color:#4f81bd;line-height:18px}

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

[b]补充知识


;SHR(Shift Right):      逻辑右移    最高位移动至次高位,然后最高位补零
;SAR(Shift Arithmetic Right): 算术右移    这种情况下,最高位是符号位,最高位移动至次高位,最高位保持不变
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: