位操作
在计算机中所有数据都是以二进制的形式储存的。位运算其实就是直接对在内存中的二进制数据进行操作,因此处理数据的速度非常快。
位操作基础
基本的位操作符有与、或、异或、取反、左移、右移这6种,它们的运算规则如下所示:
&
与
两个位都为1时,结果才为1
|
或
两个位都为0时,结果才为0
^
异或
两个位相同为0,相异为1
~
取反
0变1,1变0
<<
左移
各二进位全部左移若干位,高位丢弃,低位补0
>>
右移
各二进位全部右移若干位,高位补0或符号位补齐(有符号数,各编译器处理方法不一样,有的补符号位(算术右移),有的补0(逻辑右移))
在这6种操作符,只有~取反是单目操作符,其它5种都是双目操作符。
位操作只能用于整形数据,对float和double类型进行位操作会被编译器报错。
位操作符的运算优先级比较低,因为尽量使用括号来确保运算顺序,否则很可能会得到莫明其妙的结果。
另外位操作还有一些复合操作符,如&=、|=、 ^=、<<=、>>=。
位操作小技巧
判断奇偶 只要根据最未位是0还是1来决定,为0就是偶数,为1就是奇数。因此可以用
if ((a & 1) == 0)
代替if (a % 2 == 0)
来判断a是不是偶数。交换两数
一般的写法是:
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; }
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的值。
变换符号 变换符号就是正数变成负数,负数变成正数。 如对于-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; }
求绝对值 位操作也可以用来求绝对值,对于负数可以通过对其取反后加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; }
最后更新于
这有帮助吗?