C/C++位操作符号详解

 

一、位运算:

|  按位或操作符:result=exp1|exp2;exp1exp2中对应位中至少有一个为1时,result中对应位为1,否则为0

按位与操作符::result=exp1&exp2;exp1exp2中对应位全为1时,result中对应位为1,否则为0

^  按位异或或操作符:result=exp1^exp2;exp1exp2中对应位不相同时,result中对应位为1,否则为0

~  按位取反(反转)操作符:将位容器中的所有位都反转,1变为00变为1

|=,&=,^= 分别对应|&^三种操作符的复合操作符。

 

应用:(不用中间变量交换两个值)

void  myswap( int &a, int &b)

{

         a = a ^ b;

         b = a ^ b;

         a = a ^ b;

}

原因:a^b^b 之后,a的值不变。

另解:

 a = a + b;

 b = a - b;

 a = a - b;

但此种方法可能有溢出,没有第一种方法好。

 

 

二、定点数的移位运算

1、什么样的数据类型可以直接移位

charshortintlongunsigned charunsigned shortunsigned intunsigned long都可以进行移位操作,而doublefloatboollong double则不可以进行移位操作。

2、有符号数据类型的移位操作

对于charshortintlong这些有符号的数据类型:

  • 对负数进行左移:符号位始终为1,其他位左移 ,后面添0
  • 对正数进行左移:所有位左移,即 <<,可能会变成负数
  • 对负数进行右移:左边添1 ,右边丢弃。
  • 对正数进行右移:所有位右移,即 >>

3、无符号数据类型的移位操作

对于unsigned charunsigned shortunsigned intunsigned long这些无符号数据类型,没有特殊要说明的,使用<< >> 操作符就OK

 

移位详细原理:

定点数的移位运算也称为移位操作。左移或右移n位相当于乘以或除以2n次方。当计算机没有乘(除)运算线路时,可以采用移位和加法相结合,实现乘(除)运算。机器数字长往往是固定的,当机器数左移或右移时,必然会使其低位或高位出现空位。对空出的空位应该添补0还是1与机器数采用有符号数还是无符号数有关有符号数的移位称为算术移位无符号数的移位称为逻辑移位

(1)      算术移位:

不同码制机器数算术移位后的空位添补规则

 

  

添补代码

正数

原码、补码、反码

0

 

原码

0

负数

补码

左移添0

右移添1

  

1

  由上表可得出如下结论:

  (1)机器数为正时,不论左移或右移,添补代码均为0

  (2)由于负数的原码其数值部分与真值相同,故在移位时只要使符号位不变,其空位均添0

  (3)由于负数的反码其各位除符号位外与负数的原码正好相反,故移位后所添的代码应与原码相反,不论左移或右移,即全部添1

  (4)分析任意负数的补码可发现,当对其由低位向高位找到第一个“1”时,在此“1”左边的各位均与对应的反码相同,而在此“1”右边的各位(包括此“1”在内)均与对应的原码相同,即添0;右移时空位出现在高位,则添补的代码应与反码相同,即添1

 

2)逻辑移位:逻辑移位的规则是逻辑左移时,高位移出,低位添0;逻辑右移时,低位移出,高位添0

 

 

 

以下是一种简明的说法:

对于无符号数的移位(逻辑移位)很简单,直接变成2进制,经过移位后, 一端的位被"挤掉",而另一端空出的位以0 填补。

而对于有符号数移位(算术移位)则相对复杂一点,如果符号位为0(即正数),其移位效果与无符号数 移位一致。

容易出错的是负数移位。负数移位需先将负数变成计算机的补码形式,即保留符号位而其它位取反加1。如 int16型的-15,正常翻译为1000 0000 0000 1111,保留符号位其它位取反加1,变成1111 1111 1111 00010xFFF1)。变成这种形式后就可以对其进行移位了,左移,保留符号位,左边被挤掉的不管,右边填0;而右移的时候略有不同,保留符号位,右边被挤掉的部分丢弃,而高位填符号位1。如-15,右移3位,则变成FFFE(-2)。显然有符号数移位并不等效于乘法或除法。

如果将有符号数强制类型转换为无符号,则将其2进制补码形式翻译为无符号数即可。如

short aa = -15;

unsigned short bb;

bb = (unsigned short) aa;

bb变成0xFFF1(65521),实际上就是-15的计算机表示。

 

 

例:设机器数字长为8位(含一位符号位),若A=±26,写出三种机器数左、右移一位和两位后的表示形式及对应的真值,并分析结果的正确性。

  解:(1A=+26=(+11010)2

  则[A]=[A] =[A]=0,0011010

  移位结果表示如下:

移位操作

    

对应的真值

[A]=[A]=[A]

移位前

0,0011010

+26

左移一位

0,0110100

+52

左移两位

0,1101000

+104

右移一位

0,0001101

+13

右移两位

0,0000110

+6

  可见,对于正数,三种机器数移位后符号位不变,左移时最高数位丢1,结果出错;右移时最低数位丢1,影响精度。

  (2A=-26=(-11010)2

  三种机器数移位结果示于下表。

移位操作

    

对应的真值

移位前

1,0011010

-26

左移一位

1,0110100

-52

左移两位

1,1101000

-104

右移一位

1,0001101

-13

右移两位

1,0000110

-6

移位前

1,1100110

-26

左移一位

1,1001100

-52

左移两位

1,0011000

-104

右移一位

1,1110011

-13

右移两位

1,1111001

-7

移位前

1,1100101

-26

左移一位

1,1001011

-52

左移两位

1,0010111

-104

右移一位

1,1110010

-13

右移两位

1,1111001

-6

  可见,对于负数,三种机器数移位后符号位均不变。负数的原码左移时,高位丢1,结果出错;低位丢1,影响精度。负数的补码左移时,高位丢0,结果出错;低位丢1,影响精度。负数的反码左移时,高位丢0,结果出错;低位丢0,影响精度。

应用:(不用比较运算符来比较大小)

int mymin(int a, int b )

{

     int mask = (a - b)>>31;

     return (a & mask) | (b & ~mask);

}

 

int mymax(int a, int b )

{

     int mask = (a - b)>>31;

     return (a & ~mask) | (b & mask);

}

 

 

 

参考:http://blog.sina.com.cn/s/blog_4cc4ca890100c9c6.html

http://blog.csdn.net/howema/archive/2008/11/19/3334596.aspx

 

Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐