位寻址与掩码置位:i/8、i%8 与 1<<(7-i%8)
i/8 定位字节、7-i%8 定位 bit、
1<<造单点位掩码、|=只置位不破坏其他位。
背景
位图编码的核心一行:
它把第 i 个 bool 值"装进"字节的对应 bit 位,一行包含四个惯用法:除法定位字节、取模定位 bit、移位造掩码、按位或赋值置位。拆开看:
核心内容
三个子表达式
| 表达式 | 含义 | 例子(i=13) |
|---|---|---|
i / 8 | 第 i 位属于第几个字节(每字节装 8 位) | 13/8 = 1 → 第 1 个字节 |
i % 8 | 在该字节内是第几个位置 | 13%8 = 5 → 字节内第 5 格 |
1 << (7 - i%8) | 生成一个只有那一位是 1 的掩码 | 1<<2 = 0b00000100 |
除法与取模在这里是配对技巧:i/8 和 i%8 分别回答"第几组"和"组内第几个"。
字节的 8 格模型
一个字节就是 8 个格子,bit 编号即 2 的幂次:
out[1] 这个字节的 8 个 bit 格子:
┌───┬───┬───┬───┬───┬───┬───┬───┐
│ 7 │ 6 │ 5 │ 4 │ 3 │ 2 │ 1 │ 0 │ ← bit 编号(2 的幂次)
└───┴───┴───┴───┴───┴───┴───┴───┘
↑ ← bit 7 是最高位(最左)
1 << k 的意思是"把 1 移动到编号为 k 的格子":
|= 只置位、不破坏
|=(按位或赋值)的作用是只置位、不破坏其他位——这是设置标志位的标准手法:
位序约定:为什么是 7 - i%8
两种都能工作(只要编码解码对称),区别是字节内的排列方向:
高位在前(7 - i%8) 低位在前(i%8)
bit0 占最高位 bit0 占最低位
┌───────────────┐ ┌───────────────┐
│0 1 2 3 4 5 6 7 │ │7 6 5 4 3 2 1 0 │
└───────────────┘ └───────────────┘
out[0] = 0b10100000 out[0] = 0b00000101
= 0xA0 = 0x05
选高位在前的原因是可读性:打印十六进制/二进制时,字面上的左→右顺序与位图下标顺序一致,排查问题时"第 0 位在哪儿"一眼可见。这是纯粹的工程约定,不是协议要求。
示例
单点置位:i = 13
i = 13, bits[13] = true
i/8 = 1 → 目标是 out[1]
i%8 = 5
7-5 = 2 → 目标格子是编号 2
1<<2 = 0b00000100
out[1] |= 0b00000100
完整位图编码(12 位 → 2 字节)
bits = [T, F, T, F, F, F, F, F, F, T, F, F]
0 1 2 3 4 5 6 7 8 9 10 11 ← 下标 i
i=0: out[0] |= 1<<7 → 0b10000000
i=2: out[0] |= 1<<5 → 0b10100000
i=9: out[1] |= 1<<6 → 0b01000000
最终:
out[0] = 0b10100000 ← 装了下标 0~7
out[1] = 0b01000000 ← 装了下标 8~11(末 4 格闲置补 0)
一句话总结:i/8 找到字节,7-i%8 找到格子,1<< 造出只点亮那个格子的掩码,|= 点亮它而不碰别的格子。
注意事项
- ⚠️ 编码解码必须对称:编码用
7 - i%8,解码就必须用同一公式读,混用两种位序会得到错乱的数据 - ⚠️
uint(i%8)的显式转换在 Go 1.13+ 已非必需(有符号移位数已合法),保留多是兼容旧代码的习惯 - ✅ 掩码操作三件套:置位用
x |= mask、清位用x &^= mask、测试位用x & mask != 0 - ✅ 配套的字节容量计算
(len(bits)+7)/8见同子域的 整数向上取整惯用法
延伸阅读
- 整数向上取整惯用法:(N+7)/8 — 位打包的字节容量计算
- Go 语言规范:算术运算符
维护人:yiiewang · 最后更新:2026-09-04