跳转至

13. 位运算

位运算就是直接对整数的二进制位动手。写掩码、做移位时尽量用无符号整数,规则更直观。

13.1 六种位运算

运算符 作用 常见用途
& 按位与 保留选定位、清零其他位
| 按位或 把选定位设为 1
^ 按位异或 翻转选定位、比较位差异
~ 按位取反 所有位翻转
<< 左移 向高位移动
>> 右移 向低位移动
unsigned flags = 0;
const unsigned READ = 1u << 0;
const unsigned WRITE = 1u << 1;

flags |= READ;             /* 置位 */
flags &= ~READ;            /* 清零 */
flags ^= WRITE;            /* 翻转 */
if ((flags & WRITE) != 0) {
    /* 测试某一位 */
}

对无符号数,在没有移出有效位的前提下,左移 \(k\) 位相当于乘 \(2^k\),右移相当于除 \(2^k\)。移位数不能是负数,也不能大于等于类型本身的位数。负的有符号数移位规则不够统一,做位操作时避开它最省事。

位运算的优先级很容易记错,(x & mask) == 0 里的括号别省。

检查某一位时要用掩码。例如判断第 3 位是否为 1,可以写 if (x & 0x08);不能写成 x == 0x08,因为 x 的其他位也可能同时为 1。

13.2 位段

位段是在结构体成员后面写明要占几位:

struct status {
    unsigned ready : 1;
    unsigned error : 1;
    unsigned mode  : 3;
};

位段能紧凑地保存多个标志。不过成员究竟怎样排列、是否跨存储单元都可能随编译器改变,遇到必须精确规定每一位的文件或协议时,用无符号整数配合掩码更稳妥。

评论