c++ 位操作

本篇由作者原创(部分参考)

注意:位操作只适用于整型数据的操作,对于float型和double型数据编译器会报错。

位操作有与、或、异或、取反、左移、右移六种,符号及运算规则如下:

 符号

 描述

 运算规则                      

&      

 与

两个位都为1时,结果才为1

|  

 或    

两个位都为0时,结果才为0

^    

异或

两个位相同为0,相异为1

~   

取反

0变1,1变0

<< 

左移

各二进位全部左移若干位,高位丢弃,低位补0

>> 

右移

各二进位全部右移若干位,对无符号数,高位补0,有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移)

注意:1.位操作的优先级比较低,应该使用括号保证提前运算。

   2.对于移位操作来讲必须要注意是算术移位还是逻辑移位(算术左移、右移主要进行有符号数的倍增、倍减。逻辑左移、   右移主要进行无符号数的倍增、倍减。)

      逻辑移位:不考虑符号位,也就是不考虑正负数的移位(逻辑移位不管是左移还是右移都是空缺处补0)

      算术移位:考虑符号位,也就是正负数的移位(算数移位要保证符号位不改变)

          1)其中算术左移和逻辑逻辑左移的思想一样,缺位补0。

          2)算术右移的移位过程中,缺位由符号位决定(符号位是1,缺位补1。符号位是0,缺位补0)

注:(数据在计算机以补码存储)补码在计算机算术左移过程中符号位可能发生改变(溢出)。


位操作可以对某字节中的某段字段赋值、修改、取值(需要借助移位)等操作

实例:(这里以对字节为单位进行位操作,获取字节中的10)

#define  TEMP_NUMBER_INITINAL  0x00000000    //十六进制表示 0

#define  TEMP_NUMBER  0x000000a0   //十六进制表示 160

1 void main(){
2       int   number=NULL;
3       number = TEMP_NUMBER;
4       cout<<(number>>8)<<endl;//此时输出的是10
5 }        

位操作的一般应用:

1.判断奇偶

只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用if (a & 1 == 0)代替if (a % 2 == 0)来判断a是不是偶数。

下面程序将输出0到100之间的所有奇数。

[cpp] view plaincopy
 
  1. for (i = 0; i < 100; ++i)  
  2.     if (i & 1)  
  3.         printf("%d ", i);  
  4. putchar(' ');  

2.交换两数

一般的写法是:

[cpp] view plaincopy
 
  1. void Swap(int &a, int &b)  
  2. {  
  3.     if (a != b)  
  4.     {  
  5.         int c = a;  
  6.         a = b;  
  7.         b = c;  
  8.     }  
  9. }  

可以用位操作来实现交换两数而不用第三方变量:

[cpp] view plaincopy
 
  1. void Swap(int &a, int &b)  
  2. {  
  3.     if (a != b)  
  4.     {  
  5.         a ^= b;  
  6.         b ^= a;  
  7.         a ^= b;  
  8.     }  
  9. }  

可以这样理解:

第一步  a^=b 即a=(a^b);

第二步  b^=a 即b=b^(a^b),由于^运算满足交换律,b^(a^b)=b^b^a。由于一个数和自己异或的结果为0并且任何数与0异或都会不变的,所以此时b被赋上了a的值。

第三步 a^=b 就是a=a^b,由于前面二步可知a=(a^b),b=a,所以a=a^b即a=(a^b)^a。故a会被赋上b的值。
再来个实例说明下以加深印象。int a = 13, b = 6;

a的二进制为 13=8+4+1=1101(二进制)

b的二进制为 6=4+2=110(二进制)

第一步 a^=b  a = 1101 ^ 110 = 1011;

第二步 b^=a  b = 110 ^ 1011 = 1101;即b=13

第三步 a^=b  a = 1011 ^ 1101 = 110;即a=5

3.变换符号

变换符号就是正数变成负数,负数变成正数。

如对于-11和11,可以通过下面的变换方法将-11变成11

      1111 0101(二进制) –取反-> 0000 1010(二进制) –加1-> 0000 1011(二进制)

同样可以这样的将11变成-11

      0000 1011(二进制) –取反-> 0000 1010(二进制) –加1-> 1111 0101(二进制)

因此变换符号只需要取反后加1即可。完整代码如下:

[cpp] view plaincopy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )    
  2. #include <stdio.h>  
  3. int SignReversal(int a)  
  4. {  
  5.     return ~a + 1;  
  6. }  
  7. int main()  
  8. {  
  9.     printf("对整数变换符号 --- by MoreWindows( http://blog.csdn.net/MoreWindows )  --- ");  
  10.     int a = 7, b = -12345;  
  11.     printf("%d  %d ", SignReversal(a), SignReversal(b));  
  12.     return 0;  
  13. }  

4.求绝对值

位操作也可以用来求绝对值,对于负数可以通过对其取反后加1来得到正数。对-6可以这样:

      1111 1010(二进制) –取反->0000 0101(二进制) -加1-> 0000 0110(二进制)

来得到6。

因此先移位来取符号位,int i = a >> 31;要注意如果a为正数,i等于0,为负数,i等于-1。然后对i进行判断——如果i等于0,直接返回。否之,返回~a+1。完整代码如下:

[cpp] view plaincopy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )  
  2. int my_abs(int a)  
  3. {  
  4.     int i = a >> 31;  
  5.     return i == 0 ? a : (~a + 1);  
  6. }  

现在再分析下。对于任何数,与0异或都会保持不变,与-1即0xFFFFFFFF异或就相当于取反。因此,a与i异或后再减i(因为i为0或-1,所以减i即是要么加0要么加1)也可以得到绝对值。所以可以对上面代码优化下:

[cpp] view plaincopy
 
  1. //by MoreWindows( http://blog.csdn.net/MoreWindows )  
  2. int my_abs(int a)  
  3. {  
  4.     int i = a >> 31;  
  5.     return ((a ^ i) - i);  
  6. }
原文地址:https://www.cnblogs.com/spring-hailong/p/6071984.html