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 位段
位段是在结构体成员后面写明要占几位:
位段能紧凑地保存多个标志。不过成员究竟怎样排列、是否跨存储单元都可能随编译器改变,遇到必须精确规定每一位的文件或协议时,用无符号整数配合掩码更稳妥。