c语言中左移和右移动的原理

在嵌入式开发中,移位操作是常用的一种运算。但是在进行移位运算的时候,如果没有考虑到有符号和无符号的移位区别,就很容易掉进陷阱,得不到我们想要的结果。

我们可以看下面例子,你们猜出结果么?

signedchar i = -125;

    i= i >> 2;

    cout<< (int)i;

return 0;

编译结果为:-32

为什么有这样的结果?首先介绍两个概念:逻辑移位和算数移位。

逻辑移位,简单理解就是物理上按位进行的左右移动,两头用0进行补充,不关心数值的符号问题。

算术移位,同样也是物理上按位进行的左右移动,两头用0进行补充,但必须确保符号位不改变。

算术移位指令

算术移位指令有:算术左移SAL(ShiftAlgebraic Left)和算术右移SAR(ShiftAlgebraic Right)。算术移位指令的功能描述如下:

1)算术左移SAL把目的操作数的低位向高位移,空出的低位补0

      

2)算术右移SAR把目的操作数的高位向低位移,空出的高位用最高位(符号位)填补。

      

逻辑移位指令

此组指令有:逻辑左移SHL(ShiftLogical Left)和逻辑右移SHR(ShiftLogical Right)。逻辑左移/右移指令只有它们的移位方向不同,移位后空出的位都补0

(1)    逻辑左移SHL 


(2)    逻辑右移SHR 

 

但我们好奇的是“i<<3”“i>>3”到底采用的是算术还是逻辑移位呢?其实单从C语言本身来看可能没有太多突破,

因为C最终会被编译器编译成目标平台的汇编代码,所以必须要结合编译器和汇编程序来分析以上代码。

vs 2015中,按F9可以设置一个断点,然后调试运行,快捷键 CTRL+ALT+D进入反汇编。(本科痛苦的半个月学习的汇编,总还是用上了,嘚瑟)。

以下几种情况:

1)当i是无符号整形时,向左移动3位,采用的是逻辑左移。

unsigned int i = 8;

i = i<<3;//输出结果i = 64

 c语言中左移和右移动的原理_第1张图片

 

2)当i是有符号整形时,向左移动3位,采用的也是是逻辑左移。

int i = 8;

i = i<<3;//输出结果i = 64

 c语言中左移和右移动的原理_第2张图片

 

结论:不管是否有无符号类型,也不管值的正负,均采用的是逻辑左移。

 3)当i是无符号整形时,向右移动3位,采用的也是是逻辑右移。

unsigned int i = 8;

int main()

{

    i = i>>3;//输出结果i = 1

}

 c语言中左移和右移动的原理_第3张图片

 

4)当i是有符号整形时,向右移动3位,采用的是算术右移。

int i = 8;

int main()

{

    i = i>>3;//输出结果i = 1

    return0;

}

 c语言中左移和右移动的原理_第4张图片

 

5)负数时,当把unsigned int i =-8(32window系统下),右移动三位:

 

 c语言中左移和右移动的原理_第5张图片

6)当int i=-8时,右移动三位,结果多少呢?结果是-1

 c语言中左移和右移动的原理_第6张图片

 

结论:说明只要是有符号数,不管值是正还是负,右移时采用的都是算术右移。

疑问:按照移位补0的原则,为何左移都是逻辑移位呢?

答疑:先看看8”“8”在计算机内存中的值分别是:

0xfffffff8

0x8

由于计算机均按补码保存数值,所以不管符号正负,左移对于符号位并不产生影响,而右移则就不同了,无符号数怎么右移都不影响符号位,但是有符号数逻辑右移时高位补0将改变符号位,所以只能采用算术右移。

 

总结:只有有符号数右移才采用算术右移,否则其它情况都采用逻辑移位操作(逻辑左移或逻辑右移)。原来只要明白计算机是以补码方式保存数值的,就一切都清楚了。

 

 

你可能感兴趣的:(c语言)