对接一个硬件协议,文档里写着状态字段 flags,bit3 置 1 表示故障。同事拿整个字段和 8 比,偶尔对偶尔不对,查了两天才发现高位还有别的状态在同时置位。

标志位就是一个 bit 一个开关

一个字节八位,每一位表示一个独立的是非状态:是否在线、是否故障、是否有新数据。好处是省空间、传输快;坏处是可读性差,不看文档根本不知道第几位是什么。

取值和置位的写法

if ((flags & 0x08) != 0) { handleFault(); }

取值用与,置位用或,清位用与上取反:或上 0x08 打开,与上 0xF7 关闭。判断时一定要带不等于零,直接写 if (flags & 0x08) 在 C 里能过,在强类型语言里可能编译不过。

别用等号判等

flags == 0x08 只在其他位全是 0 时成立。协议里高位往往还挂着别的状态,一旦同时置位就漏判。这是标志位最典型的错误,也是那位同事卡两天的原因。

CPU 里也有一组标志位

x86 的 EFLAGS 寄存器,CF 管进位、ZF 表示结果为零、SF 是符号、OF 是溢出。比较指令本质就是做减法再改这些位,汇编里的 jz、jnz、jc 都按标志位跳转。理解了这个,看反汇编时那些 test 和 cmp 就不陌生了。

业务里怎么用

权限常按 1、2、4、8、16 依次翻倍,一个整数存全部权限,判断用与运算。C# 用 Flags 特性加枚举,Java 用 EnumSet 更安全。数据库存一个整数比开十列省事,但按某个权限查询要写位运算,索引基本用不上,数据量一大就慢。

几个容易翻车的地方

  • 位宽不够,int 存了三十多个标志,再加就没位了
  • 有符号数右移补符号位,取高位标志时结果变成负数
  • 协议两端位序约定相反,0x01 在一端代表完全不同的含义

日志里打印成二进制

直接输出十进制没人看得懂。转成八位二进制,0b00001000 一眼就知道是第三位;排查时把常用位映射成字符串,打印出 online、fault 这种可读结果,比对着文档数位快。

标志位适合状态少、变化频繁的地方;超过十几种状态,拆成独立字段或者关联表更清楚,省下来那几个字节不值当。