按位运算符分为按位逻辑运算符和位移运算符。
按位逻辑运算符
按位逻辑运算符有按位与(&)、按位或(|)、按位异或(^)、按位取反(~),除了按位取反(~)运算符的优先级比较高(PS:低于()、[]、.)之外,其他三个按位逻辑运算符的优先级都比较低(PS:低于关系运算符,高于逻辑运算符)。
之所以称这四个运算符为按位逻辑运算符,是因为这些操作是对整数的每一个位进行操作,而不是整个值进行操作,这一点要和逻辑运算符区分开。
介绍
按位取反(~)
一元运算符 ~ 对整数的每一位将1变为0,0变为1。
// 表达式
~(1001 1010)
// 结果值
(0110 0101)
按位取反运算符常用于求二进制反码。
按位与(&)
按位与运算符 & 逐位比较两个运算对象的每一位,生成新的值。对于每个位,只有两个运算符对象的相应位都为 1 时,结果才为1 (从真假来看,都真才真)。
// 表达式
(1001 0011) & (0011 1101)
// 结果
(0001 0001)
// 所有的运算结果
0 & 0 == 0
0 & 1 == 0
1 & 0 == 0
1 & 1 == 1
从运算结果中,我们可以得到以下的性质。
性质: 假设 b 是某个二进制值的某位。
1. 任意位和 1 做 & 运算的结果是它本身,即 b & 1 = b。
2. 任意位和 0 做 & 运算的结果是 0,即 b & 0 = 0。
利用上面的性质,我们可以用 & 运算符来关闭指定位(将指定位设置为 0,其他位不变),详情见应用。
按位或(|)
按位或运算符 | 逐位比较两个运算对象的每一位,生成新的值。对于每个位,只有两个运算符对象的相应位都为 0 时,结果才为0 (从真假来看,都假才假)。
// 表达式
(1001 0011) | (0011 1101)
// 结果值
(1011 1111)
// 所有的运算结果
0 | 0 == 0
0 | 1 == 1
1 | 0 == 1
1 | 1 == 1
从运算结果中,我们可以得到以下的性质。
性质: 假设 b 是某个二进制值的某位。
1. 任意位和 1 做 | 运算的结果是 1,即 b | 1 = 1。
2. 任意位和 0 做 | 运算的结果是它本身,即 b | 0 = b。
利用上面的性质,我们可以用 | 运算符来打开指定位(将指定为设置为1,其他位不变),详情见应用。
按位异或(^)
按位异或运算符 ^ 逐位比较两个运算对象的每一位,生成新的值。对于每个位,只有两个运算符对象的相应位不同时,结果才为1 (从真假来看,不同时为真或者不同时为假结果才为真)。
// 表达式
(1001 0011) ^ (0011 1101)
// 结果
(1010 1110)
// 所有的运算结果
0 ^ 0 == 0
0 ^ 1 == 1
1 ^ 0 == 1
1 ^ 1 == 0
从运算结果中,我们可以得到以下的性质。
性质: 假设 b 是某个二进制值的某位。
1. 任意位和 1 做 ^ 运算的结果是该位取反,即 b ^ 1 = ~b。
2. 任意位和 0 做 ^ 运算的结果是本身,即 b ^ 0 = b。
利用上面的性质,我们可以用 & 运算符来切换指定位(将指定位取反,其他位不变),详情见应用。
应用
掩码(& 应用)
所谓掩码指的是一些设置为开(1)或者关(0)的位组合。
我们先看一下通过 & 把一个量与掩码结合后会如何。例如,假设定义一个符号常量 MASK 为 2 (即, 0000 0010)。那么,flags = flags & MASK 会将 flags 中除 1 号位之外的所有位都设置为 0。
所以掩码和 & 运算符结合就是将 flags 对应掩码为0的位掩埋起来(设置位0),因为我们不需要这些位的值,只关注掩码中值为1的位。
用途:假设我们用1个8位二进制数的每个位代表一个状态,1表示开启,0表示关闭,比如 1001 0101,而我们在操作的过程中可能只关注某一部分状态是否正确,而其他状态不需要关注,这时候就可以用掩码对不需要关注的位进行屏蔽,这样更容易法进行操作。
例如,flags 中 1 号位表示可写,检查 flags 变量是否表示可写,不能直接比较 flags 和 MASK if (flags == MASK)
这样的话,flags 为 0000 0011,1 号位是1,但是判断条件依旧为 false。应该先使用掩码将无关位屏蔽,再进行判断,即 if ((flags & MASK) == MASK)
。
掩码在网络中应用很常见,子网掩码。
打开指定位(设置位)
利用 | 运算符的性质,我们可以在不改变其他位的情况下,将指定位设置为 1。
假设现在有一个 flags 值为 0000 1111,希望将其1、2、4、5、7号为设置为 1,其他位不变,所以 MASK 为 1011 0110。
// 表达式 flags | MASK
(0000 1111) | (1011 0110)
// 结果值
(1011 1111)
关闭指定位(清空位)
利用 & 运算符的性质,我们可以在不改变其他位的情况下,将指定位设置为 0。
假设现在有一个 flags 值为 0000 1111,希望将其1、2、4、5、7号为设置为 0,其他位不变,所以 MASK 为 0100 1001。
// 表达式 flags & MASK
(0000 1111) & (0100 1001)
// 结果值
(0000 1001)
PS:也可以将 MASK 设为 1011 0110,利用 ~ 取反,在用 & 运算。
切换指定位
利用 ^ 运算符的性质,我们可以在不改变其他位的情况下,将指定位进行切换,即 0 变为 1,1 变为 0。
假设现在有一个 flags 值为 0000 1111,希望切换其1、2、4、5、7号的值,其他位不变,所以 MASK 为 1011 0110。
// 表达式 flags ^ MASK
(0000 1111) ^ (1011 0110)
// 结果值
(1011 1001)
位移运算符
位移逻辑运算符有左移(<<)、右移(>>)、无符号右移(>>>),其优先级低于算术运算符,高于关系运算符。
介绍
左移运算符 <<
左移运算符将左侧运算对象每一位的值向左移动其右侧运算对象指定的位数。左侧运算对象移出左末端位的值丢失,用 0 填充空位。
// 表达式
(1000 1010) << 2
// 结果值
(0010 1000)
右移运算符 >>
右移运算符将左侧运算对象每一位的值向右移动其右侧运算对象指定的位数。左侧运算对象移出右末端位的值丢失。对于无符号类型,用 0 填充空出位置。对于有符号类型,取决于机器,空出位置可以用 0 填充,或者用符号位。
应用
位移运算符可以针对 2 的幂提供快速有效的乘除法。