众所周知,计算机内部使用二进制存储数据。同样,C++中也有很多位运算符:
运算符 | 含义 | 应用示例 |
~ | 按位取反 | int b = ~a; |
& | 按位与 | int b = a & 1; |
| | 按位或 | int b = a | 1; |
<<, >> | 左移/右移 | int b = a >> 114 << c; |
^ | 按位异或 | int b = a ^ 514; |
一、原码、反码、补码
在具体介绍这些运算符之前,我们要先了解一个数在二进制下的不同形式:
原码,即一个数在二进制下直接转化的结果,例如5的原码在32为整数类型下为:
00000000 00000000 00000000 00000101
反码,即对于原码的所有位,改变其数值(1->0, 0->1),如5的反码为:
11111111 11111111 11111111 11111010
对于整数,补码就是它本身;对于负数,补码即一个数的反码加1,如-5的补码为:
11111111 11111111 11111111 11111011
计算机在存储数据时,会留下一位符号位,以补码储存。例如32位整数的第一位是符号位,如果为0代表正数,否则代表负数;-5在计算机存储为:
1 1111111 11111111 11111111 11111011
^
符号位
二、运算符的意义和应用
1、~按位取反
没有什么实际用途,对于二进制数的每一位,0变成1,1变成0:
~(114)10 = ~(00000000 00000000 00000000 01110010)2
= (11111111 11111111 11111111 10001101)2 = (-115)10
(以补码形式出现,转成原数时先减1再取反)
2、&按位与
a & b:对于二进制数a的每一位,对二进制数b的相应位置进行与操作:两者都为1时结果为1,否则结果为0。
例如,114 & 514 = 0000 0111 0010 & 0010 0000 0010 = 0010 = 2
应用:首先是lowbit()函数,树状数组必备,即 lowbit(x) = x & (-x)。
-x即x的反码+1,这个操作将会去掉反码最后连续的1,即去掉原码最后连续的0,给前一位置1,但前一位一定是0,因此,x & (-x) 就是去掉x在二进制下的最后一个1。
同时,按位与可以快捷对2取余,即x&1,适合装13。
3、<<,>>左移、右移
通俗易懂地说,a << n 就是 a * 2的n次方,右移n位就是除以2的n次方。
对,就没了。
具体地说,就是这样:
左移:5 << 2 = 101 << 2 = 10100
//所有位左移两位,空位补0
右移:5 >> 2 = 101 >> 2 = 1
//所有位右移两位,前面补0
4、| 按位或
a | b:对于二进制数a的每一位,对二进制数b的相应位置进行或操作:两者都为0时结果为0,否则结果为1。
例如,114 | 514 = 0000 0111 0010 | 0010 0000 0010 = 0111 0010 = 626
用于把某些位置1,如5 | (1 << n)就是把二进制下的第n-1位置为1
5、^ 按位异或
a ^ b:对于二进制数a的每一位,对二进制数b的相应位置进行或操作:两者都为0或都为1时结果为0,否则结果为1。
性质:a ^ b ^ b = a,a ^ b ^ c = a ^ c ^ b = b ^ c ^ a
用按位异或可以实现交换数字:
int a, b;
cin >> a >> b;
a = a ^ b; //a = a ^ b
b = a ^ b; //b = a ^ b ^ b = a
a = a ^ b; //a = a ^ b ^ a = b
同时,异或还可以实现数据的加密,这里不做解释。
----------------------------------------------------------
至此,这篇文章就告一段落了。
最后,制作不易,如果你觉得这篇文章还不错的话,记得点个赞,点个收藏,点个关注。这是免费的,您随时可以取消,感谢大家的支持!