位操作

在计算机中所有数据都是以二进制的形式储存的。位运算其实就是直接对在内存中的二进制数据进行操作,因此处理数据的速度非常快。


位操作基础

基本的位操作符有与、或、异或、取反、左移、右移这6种,它们的运算规则如下所示:

符号
描述
运算规则

&

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

|

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

^

异或

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

~

取反

0变1,1变0

<<

左移

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

>>

右移

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

  1. 在这6种操作符,只有~取反是单目操作符,其它5种都是双目操作符。

  2. 位操作只能用于整形数据,对float和double类型进行位操作会被编译器报错。

  3. 位操作符的运算优先级比较低,因为尽量使用括号来确保运算顺序,否则很可能会得到莫明其妙的结果。

  4. 另外位操作还有一些复合操作符,如&=、|=、 ^=、<<=、>>=。

位操作小技巧


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

  2. 交换两数

    一般的写法是:

    void swap(int a, int b) {
        int c = a;
        a = b;
        b = c;
    }

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

    void swap(int a, int b) {
        a ^= b;
        b ^= a;
        a ^= b;
    }
    1. a^=b 即a=(a^b);

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

    3. a^=b 就是a=a^b,由于前面二步可知a=(a^b),b=a,所以a=a^b即a=(a^b)^a。故a会被赋上b的值。

  3. 变换符号 变换符号就是正数变成负数,负数变成正数。 如对于-12和12,可以通过下面的变换方法将-12变成12

    1111 0100 取反> 0000 1011 加1> 0000 1100 -12 取反> 11 加1> 12

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

    0000 1100 取反> 1111 0011 加1> 1111 0100 12 取反> -13 加1> -12

    因此变换符号只需要取反后加1即可。

    int reversal(int a) {
        return ~a + 1;
    }
  4. 求绝对值 位操作也可以用来求绝对值,对于负数可以通过对其取反后加1来得到正数。

    1111 1010 取反> 0000 0101 加1> 0000 0110 -6 取反> 5 加1> 6

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

    int abs(int a) {
        return a >> 31 == 0 ? a : (~a + 1);
    }

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

    int abs(int a) {
        int i = a >> 31;
        return (a ^ i) - i;
    }

最后更新于

这有帮助吗?