位运算符(选读)
位运算符包含以下六种:
运算符 | 名称 | 作用 |
---|---|---|
a & b | 逐位与运算符 | 两侧操作数逐位进行与运算 |
a | b | 逐位或运算符 | 两侧操作数逐位进行或运算 |
a ^ b | 逐位异或运算符 | 两侧操作数诸位进行异或运算 |
~a | 取反运算符 | 将操作数逐位取反 |
a << b | 左移运算符 | 将左侧操作数内存左移 |
a >> b | 右移运算符 | 将左侧操作数内存右移 |
下面我们来分别介绍它们。
逐位与运算符
逐位与表达式的写法如下:
左侧操作数 & 右侧操作数
将参加运算的两个操作数的每个二进制位进行独立的与运算。若类型不同,进行如同算术运算一样的类型转换。例如 3 & 5
,即 0b011 & 0b101
(之前提到过, 0b
前缀代表二进制。),它的运算过程是这样的:
得到的结果为 1
。
逐位或运算符
逐位或表达式的写法如下:
左侧操作数 | 右侧操作数
将参加运算的两个操作数的每个二进制位进行独立的或运算。若类型不同,进行如同算术运算一样的类型转换。例如 3 | 5
,即 0b011 | 0b101
,它的运算过程是这样的:
得到的结果为 7
。
逐位异或运算符
异或运算在日常生活中貌似比较少见。以下是异或运算的结果:
XOR(异或) | 1 | 0 |
---|---|---|
1 | 0 | 1 |
0 | 1 | 0 |
异或运算的作用其实非常大。你可以阅览这份 知乎回答 来获取更直观的理解。
可以看出,异或即两位是否不同。逐位异或表达式的写法如下:
左侧操作数 ^ 右侧操作数
将参加运算的两个操作数的每个二进制位进行独立的异或运算。若类型不同,进行如同算术运算一样的类型转换。例如 57 ^ 42
即 0b111001 ^ 0b101010
,它的运算过程是这样的:
(其中 是异或的数学记号)得到的结果为 19
。
取反运算符
取反表达式的写法如下:
~操作数
将操作数的二进制表示逐位取反。即 1
变为 0
, 0
变为 1
。例如:对于
则 ~us
的值为 0b1111111111101010
,即 65514
。
左移运算符和右移运算符
左移表达式和右移表达式的写法如下:
左侧操作数 << 右侧操作数 左侧操作数 >> 右侧操作数
其作用为求出将左侧操作数的各个二进制位向左/右移动右侧操作数那么多位的值。其中,左侧操作数将进行整型提升(参见算术运算符章节注释)。右侧操作数需为非负数。(若右侧操作数为负数,则行为未定义。)
对于左移运算,左侧溢出部分舍去,右侧用 0
补齐;对于右移运算,右侧溢出部分舍去,左侧采用如下的办法补齐:
- 若原数无符号,则用
0
补齐; - 若原数有符号,非负数用
0
补齐,负数用1
补齐。
左移和右移运算可简单地概括为:左移运算左侧操作数乘 并舍去溢出部分,右移运算左侧操作数除以 并向零取整。其中 为右侧操作数的值。例如:
42 << 2
即0b101010 << 2
,结果为0b10101000
,即 ;42 >> 2
即0b101010 >> 2
,结果为0b1010
,即 。(中括号为取整(高斯)函数。)
复合赋值运算符的扩充
位运算符也可以与赋值运算相结合。其含义与我们学过的复合赋值运算符相仿。
运算符 | 名称 | 作用 |
---|---|---|
a &= b | 逐位与复合赋值运算符 | 逐位与后赋值给左侧数 |
a |= b | 逐位或复合赋值运算符 | 逐位或后赋值给左侧数 |
a ^= b | 逐位异或复合赋值运算符 | 逐位异或后赋值给左侧数 |
a <<= b | 左移复合赋值运算符 | 左移后赋值给左侧数 |
a >>= b | 右移复合赋值运算符 | 右移后赋值给左侧数 |
这些符合赋值表达式的写法为:
左侧数 位运算符= 右侧数
它等价于
左侧数 = 左侧数 位运算符 右侧数
位运算的作用
由于位运算在计算机中的运行速度最快,因此使用带有位运算的技巧可以显著提升程序效率,但相应的可能会有可读性的损失。
获取某变量的某二进制位
我想知道 int
型变量 a
的第 x
位是 0
还是 1
(最低位为第 0
位),可以用这个表达式:
bool(a & (1 << x))
这是因为, 1 << x
会得到一个仅第 x
位为 1
,而其余位全为 0
的值。然后它去和 a
做“与”运算,其它位全部归零,只有第 x
位被保留。因此,若第 x
位为 0
,则上述表达式结果为 false
,否则为 true
。
还有一种写法
1 & (a >> x)
也可得到一样的结果,而且可以不用转型。请读者自行推断这个表达式的含义。
快速交换变量
若要交换 a
和 b
两个变量的值,可以使用:
a ^= b;
b ^= a;
a ^= b;
这样的写法而不借助第三个变量。具体的原理是:
异或具有交换律和结合律(请读者自行验证),且对于任意数 显然满足以下特性:
- ;
- 。
我们用 和 表示运算前 a
和 b
的值,则
- 第一行执行后,
a
的值为 ,b
的值为 ; - 第二行执行后,
a
的值为 ,b
的值为 ; - 第三行执行后,
a
的值为 ,b
的值为 。
这时, a
的值
而 b
的值
这就证明了交换的正确性。
不过不建议在程序中使用这样的写法,因为可读性较差且有致命的陷阱:若 a
和 b
指代同一变量时,该方法将失效。(相当于做了三次 a ^= a
,结果 a
被清零。)