位运算与掩码

共 19 题
📑 题目列表 19 题
#
★★★

1. 用掩码 0xFF 提取一个 32 位整数的最低字节,写出位运算表达式?

请用掩码 0xFF 提取一个 32 位整数的最低字节,并写出位运算表达式?

  • 掩码提取
  • 与运算
  • 右移配合掩码提取任意字节

用按位与 x & 0xFF 即可提取最低字节。因为 0xFF = 11111111,与任何一个整数相与后,高 24 位被清零,只保留最低 8 位。例如 x = 0x12345678,x & 0xFF = 0x78。若想提取其它字节,可先右移再与掩码,如 (x >> 8) & 0xFF 提取第二字节。

掩码与运算是最常用的字节/位提取手段。0xFF 定位最低字节,配合右移可提取任意字节。

#
★★★

2. 解释 (n & 1) 判断奇偶性的位运算原理?

请解释 (n & 1) 判断奇偶性的位运算原理?

  • 最低位含义
  • 位运算判断奇偶
  • 最低位 2^0 决定奇偶性

二进制的最低位(2^0)决定奇偶性:最低位为 1 是奇数,为 0 是偶数。n & 1 只保留最低位,其余位清零。若结果为 1 则是奇数,为 0 则是偶数。例如 5 = 101,5 & 1 = 1,奇数;6 = 110,6 & 1 = 0,偶数。相比取模 n % 2,位运算对负数行为也一致且更快。

与 1 相与等价于取最低位。该技巧效率高,且避免了取模对负数符号的潜在差异。

#
★★★

3. 解释位运算 (x ^ y) 为 0 当且仅当 x == y,结合按位异或性质?

请解释位运算 (x ^ y) 为 0 当且仅当 x == y,结合按位异或的性质?

  • 异或性质
  • 相等判定
  • 异或自反性 x ^ x = 0

异或(^)的规则是相同位得 0、相异位得 1。因此 x ^ y 的每一位表示 x 与 y 对应位是否不同。若 x == y,则所有位相同,x ^ y = 0;若 x != y,则至少有一位不同,x ^ y 非 0。所以 x ^ y == 0 当且仅当 x == y。这是判断相等的一种位运算方式,且异或还有自反性:x ^ x = 0,x ^ 0 = x。

异或逐位比较,全 0 说明全等。这一性质也用于异或交换、异或加密与奇偶校验。注意它与比较运算结果等价,但读法不同。

#
★★★

4. 把 0xCAFEBABE 与 0xFFFFFFFF 按位与,结果十六进制是?

把 0xCAFEBABE 与 0xFFFFFFFF 按位与,结果十六进制是多少?

  • 全 1 掩码与运算
  • 与单位元
  • 与 0 清零、与 1 保留的位运算性质

0xFFFFFFFF 是 32 位全 1。任何数与全 1 相与结果仍是它本身,因为每个位与 1 相与保持不变。所以 0xCAFEBABE & 0xFFFFFFFF = 0xCAFEBABE。全 1 是与运算的单位元。

与 1 相与保留位,与 0 相与清零位。全 1 掩码相当于取原值,是位运算的基本性质。

#
★★★

5. 如何用位运算 (n & (n-1)) == 0 判定 2 的幂次?

请用位运算判断一个数是否为 2 的幂次,即 (n & (n-1)) == 0 的原理?

  • n & (n-1) 清除最低位 1
  • 2 的幂判定
  • 需排除 n = 0 的特判

n & (n-1) 会清除 n 的最低一个 1 位。若 n 是 2 的幂,则二进制只有一个 1(如 8 = 1000),n-1 = 0111,两者相与 = 0。若 n 有多个 1,则 n & (n-1) 非 0。因此 (n & (n-1)) == 0 判定 n 是 2 的幂(需额外排除 n == 0)。例如 16 & 15 = 0,16 是 2 的幂;12 & 11 = 8 ≠ 0。

n-1 会把最低位 1 及其后所有位取反,与 n 相与后这些位全为 0。只有单个 1 时才全 0。实际判断应为 n > 0 && (n & (n-1)) == 0。

#
★★★

6. lowbit 运算 n & (-n) 的原理与推导,12(0b1100)的 lowbit 是多少,树状数组为何依赖它?

请解释 lowbit 运算 n & (-n) 的原理与推导,给出 12(0b1100)的 lowbit,并说明树状数组为何依赖它?

  • lowbit 原理
  • 树状数组区间
  • 补码性质定位最低位 1

lowbit(n) = n & (-n) 返回 n 中最低位 1 所代表的值。推导:n 的最后一个 1 位为 1 后跟 k 个 0,即 2^k。-n 是 n 的补码(取反加一),其最低 k 位为 0,第 k 位为 1,更高位与 n 取反。因此 n & (-n) 恰好保留最低位 1 及其低位 0,其余清零,得到 2^k。对 12 = 1100,lowbit = 2^2 = 4。树状数组用 lowbit 决定每个节点覆盖的区间长度(下标 i 覆盖 [i-lowbit(i)+1, i]),从而支持 O(log n) 前缀和与更新,是树状数组的核心。

lowbit 通过补码性质定位最低位 1。树状数组每个节点 i 管理长度为 lowbit(i) 的区间,区间更新与查询都沿 lowbit 跳跃,保证 O(log n)。这是树状数组依赖 lowbit 的原因。

#
★★

7. 位移在序列化中的位级优化(如 Flags 字段打包到 int)的应用与可读性取舍

请说明位移在序列化中的位级优化(如把 Flags 字段打包到 int)的应用,以及可读性取舍?

  • 位打包
  • 可读性取舍
  • 用命名常量提升可读性

位打包把多个布尔标志字段压缩到一个 int 中,用位表示每个标志。例如用 (1 << k) 表示第 k 个标志,用 | 设置、& 判断、~ 清除。序列化时只需传输一个 int,节省空间。但这以可读性为代价:字段失去语义名,代码晦涩,需定义常量宏/枚举。取舍是:空间敏感、批量标志场景用位打包;可读性优先时用结构体或位域。

位打包在协议、权限、配置项中常用,节省带宽与内存。但需用命名常量提升可读性,并文档化每一位含义。位域(bit-field)是 C 的语言级支持。

#
★★

8. 用 (n & (n-1)) 清除最低位的 1,写出对 0b10110100 运算的结果?

请用 (n & (n-1)) 清除最低位的 1,写出对 0b10110100 运算的结果?

  • n & (n-1) 清除最低位 1
  • 运算结果
  • 借助该技巧统计 1 的个数

n = 0b10110100,最低位 1 在位置 2(值为 4)。n-1 = 0b10110011,n & (n-1) = 0b10110100 & 0b10110011 = 0b10110000。结果清除了最低位 1(原第 2 位的 1 被清除),其余位不变。所以结果为 0b10110000。

n-1 把最低位 1 变为 0,把其后的 0 变为 1,与原数相与后,最低位 1 及其后位被清零,达到清除最低位 1 的效果。常用于统计 1 的个数。

#
★★

9. 给定 8 位 0b00001111 << 4 的结果是什么?移出最高位会怎样?

给定 8 位 0b00001111 << 4,结果是什么?移出最高位会怎样?

  • 左移运算
  • 溢出丢弃
  • 左移等价乘 2 及溢出时的模 2^n 行为

0b00001111 << 4 = 0b11110000,即 240。左移在右侧补 0。移出最高位的位会被丢弃(超出位数范围的部分丢失)。若考虑 8 位有符号,则 0b11110000 补码为 -16,符号翻转。无符号下 0b11110000 = 240。左移 1 位等价乘 2,但溢出时等价于模 2^n。

左移补 0,超出的高位丢弃。对无符号是模 2^n 乘法,对有符号位宽不足时可能溢出。注意左移不能移超过位宽(否则 UB)。

#
★★

10. 给定 8 位 0b10110100 与 0b11001010,求 AND/OR/XOR 三种结果?

给定 8 位 0b10110100 与 0b11001010,求 AND、OR、XOR 三种结果?

  • 按位与、或、异或
  • 逐位运算
  • 逐位独立运算的规则

逐位运算:AND:10110100 & 11001010,逐位与得 10000000(0b10000000)。OR:逐位或得 11111110(0b11111110)。XOR:逐位异或得 01111110(0b01111110)。结果:AND=0b10000000,OR=0b11111110,XOR=0b01111110。

按位运算逐位独立。AND 只有两位都为 1 才为 1,OR 任一位为 1 即 1,XOR 两位不同才为 1。逐位核对即可。

#
★★

11. 常用位运算技巧有哪些,如 n&(n-1)、lowbit、异或交换与取反?

请总结常用位运算技巧,包括 n&(n-1)、lowbit、异或交换与取反?

  • 常用位运算技巧
  • 各技巧的原理
  • 异或交换依赖自反性无需临时变量

n & (n-1) 清除最低位 1,用于判断 2 的幂、统计 1 的个数。lowbit = n & (-n) 取最低位 1 的值,用于树状数组。异或交换:a = a^b; b = a^b; a = a^b 可交换两数,无需临时变量,基于异或自反性。取反:~x 按位取反,~x + 1 = -x 得到相反数。还有 n & 1 判奇偶、x ^ x = 0 等。

这些技巧基于补码与异或的代数性质。n&(n-1) 与 lowbit 用于计数与区间,异或交换与取反用于无临时变量操作。掌握原理即可灵活运用。

#
★★

12. 位图与布隆过滤器如何实现海量数据的去重与判断?

请说明位图与布隆过滤器在海量数据的去重与是否存在判断中的应用?

  • 位图
  • 布隆过滤器
  • 布隆过滤器误报率与哈希函数关系

位图(bitmap)用每一位表示一个元素是否存在,适合元素范围已知且稠密的情形,内存极小(1 字节 8 个元素),支持去重与成员判断。布隆过滤器用多个哈希函数把元素映射到多个位,所有位都为 1 表示可能存在,有则为不存在,用于海量数据判断是否存在(如 URL 去重、缓存穿透过滤),误报率可调但不可负误报。位图精确但要求范围小,布隆节省空间但可能误报。

位图是精确的稠密集合,布隆是概率型稀疏集合。海量场景,位图内存受限时用布隆过滤,牺牲少量误报换取超低内存。

#
★★

13. 生成 n 位全 1 掩码 (1 << n) - 1 时,n=32 直接 1<<32 是未定义行为,工程上如何安全构造?

生成 n 位全 1 掩码 (1 << n) - 1,当 n=32 时直接 1<<32 是未定义行为,工程上如何安全构造?

  • 移位位数边界
  • 安全构造掩码
  • 用 ~0U 构造全 1 再按需移位

此例中要生成 32 位全 1 掩码(0xFFFFFFFF)。直接 (1 << 32) - 1 会因移位 32 位(等于位宽)而为 UB。安全构造方法:用 ~0U 得到全 1 并右移获得任意宽度,如 ((~0U) >> (32 - n)) 生成 n 位低位全 1;或对 n=32 特判直接返回 ~0U;或使用 ((1ULL << (n-1)) - 1) * 2 + 1 等技巧避免移满位宽。最简单:((~0U) >> (32 - n)) 当 n=32 时右移 0 位得全 1。

移位位数等于或超过位宽是 UB。安全的做法是构造全 1 再按需移位,避免直接 1<<n。用无符号类型避免符号位问题。

#
★★

14. 位运算实现绝对值 abs(x) = (x ^ (x>>31)) - (x>>31),解释对正数、负数与 INT_MIN 的分别行为?

请解释位运算实现绝对值 abs(x) = (x ^ (x>>31)) - (x>>31),并说明它对正数、负数与 INT_MIN 的分别行为?

  • 算术右移符号扩展
  • 位运算绝对值与边界
  • 算术右移生成全 1 掩码

x>>31 对有符号数是算术右移,负数时全 1(-1),正数时全 0。设 mask = x>>31。若 x 为正,mask=0,x ^ 0 - 0 = x。若 x 为负,mask=-1(全 1),x ^ (-1) = ~x,(x ^ mask) - mask = ~x - (-1) = ~x + 1 = -x,得到绝对值。对 INT_MIN,x>>31 = -1,~INT_MIN + 1 = INT_MIN(因为 -INT_MIN 溢出回绕),abs 仍为负的 INT_MIN,无法得到正数,故对 INT_MIN 结果是未定义/溢出。

该技巧用算术右移生成全 1 掩码,通过异或与减法实现取绝对值。但 INT_MIN 无对应正数,数学上溢出,需特殊处理。

#

15. 解释为何负数右移在 C 中是 implementation-defined?

请解释为什么负数右移在 C 中是 implementation-defined?

  • 右移语义
  • 负数右移实现定义
  • 依赖编译器实现使代码可移植性差

C 标准规定,对右移,若操作数为非负,则结果由除法定义(逻辑右移,高位补 0);若为负数,则结果是 implementation-defined,即由编译器实现决定用算术右移(高位补符号位)还是逻辑右移(高位补 0)。绝大多数现代平台(如 x86、ARM)对负数右移实现为算术右移,但标准不保证,因此依赖它的代码可移植性差。

语义未统一是因为历史硬件差异。可靠代码应避免依赖负数右移的具体行为,或在保证平台算术右移的前提下使用。

#

16. 位掩码与标志位中,如何用位域压缩多个布尔状态?

请说明如何用位掩码与标志位,即用位域压缩多个布尔状态?

  • 位域
  • 标志位压缩
  • 位掩码的与判断、或设置、异或清除

位域(bit-field)在 C/C++ 中允许为结构体成员指定位宽,如 struct { unsigned int a:1; unsigned int b:1; unsigned int c:2; } 这样用 4 位存 3 个状态。也可用位掩码:每个标志定义常量 (1<<k),用 | 设置、& 判断、~ 清除。共同原理是把多个布尔值压缩到同一个整数各不重叠的位,节省内存、便于批量传输与比较。

位域是语言级支持,位掩码是手动管理。两者都省空间,但位域布局与内存对齐依赖实现,位掩码更可控。适合状态标志、权限位等场景。

#

17. 字节序(大小端)中,网络序与主机序如何转换?

请说明字节序(大小端),以及网络序与主机序的转换?

  • 大小端
  • 网络序转换
  • htonl/htons 与 ntohl/ntohs 转换函数

字节序决定多字节整数在内存中的字节排列。大端(BE)高字节在低地址,小端(LE)低字节在低地址。网络字节序规定为大端(BE)。主机若为小端,则发送前需用 htonl/htons 把主机序转成网络序,接收后用 ntohl/ntohs 转回。因为网络协议(如 IP、TCP)跨不同字节序平台,统一为大端保证可移植。

网络序统一大端,避免跨平台歧义。htonl/ntohs 等函数在主机序与网络序之间转换。判端可用联合体或指针检查首字节。

#

18. 位运算在权限管理中的应用,位掩码如何与权限组合?

请说明位运算在权限管理中的应用,即位掩码与权限组合?

  • 权限位掩码
  • 权限组合与判断
  • 用命名权限常量提升可读性

权限管理用每个位表示一种权限,如 READ=1<<0、WRITE=1<<1、EXEC=1<<2。组合权限用 |(如 READ|WRITE),判断是否有某权限用 &(如 perm & READ != 0),追加用 |,移除用 perm & ~READ。Unix 文件权限、RBAC 常以此实现。这样权限集可压缩为一个整数,判断高效。

位掩码实现权限的集合运算:或即并集,与即交集/判断,取反与即差集。配合命名常量提升可读性。

#

19. Brian Kernighan 位计数与 POPCNT 指令统计 1 的个数分别是 O(k) 与 O(1),何时应使用硬件指令?

请说明 Brian Kernighan 位计数与 POPCNT 指令,统计 1 的个数分别是 O(k) 与 O(1),何时应使用硬件指令?

  • Kernighan 位计数
  • POPCNT 硬件指令
  • __builtin_popcount 自动映射到硬件指令

Brian Kernighan 位计数用 while (n) { n &= n-1; count++; },每次循环清除一个最低位 1,循环次数等于 1 的个数 k,复杂度 O(k)。POPCNT 是硬件指令,单条指令统计所有位的 1,复杂度 O(1)(常数时间),在 x86(SSE4.2)、ARM(NEON)等 CPU 上可用。当数据量小、循环开销可忽略时用 Kernighan 即可;当性能敏感、大量位计数时用 __builtin_popcount(编译器映射到 POPCNT)或 __builtin_popcountll。

Kernighan 简单可移植,复杂度 O(k)。POPCNT 常数时间更快,但依赖硬件支持。现代编译器的 __builtin_popcount 在有 POPCNT 时自动生成指令,否则退化为循环。性能敏感场景优先硬件指令。