6月1日 14:52

C语言位运算符怎么用?设置、清除、翻转一位怎么做?

C 语言有 6 个位运算符:&(与)、|(或)、^(异或)、~(取反)、<<(左移)、>>(右移)。位运算直接操作二进制位,在标志位管理、数据压缩、硬件寄存器操作中不可替代。

追问

位运算怎么设置、清除、翻转、检查某个位?

这是位运算面试的必考题,记住四个套路:

c
#define BIT(n) (1 << n) flags |= BIT(3); // 设置第 3 位 flags &= ~BIT(3); // 清除第 3 位 flags ^= BIT(3); // 翻转第 3 位 if (flags & BIT(3)) {} // 检查第 3 位是否为 1

原理:| 遇 1 则 1(设置),& ~ 把目标位变 0 其余不变(清除),^ 遇 1 翻转遇 0 不变(翻转),& 提取目标位(检查)。

异或有什么特殊性质?

三个重要性质:1) 任何数异或自己为 0(a ^ a == 0);2) 任何数异或 0 不变(a ^ 0 == a);3) 交换律和结合律。实际应用:不用临时变量交换两个数(a ^= b; b ^= a; a ^= b;)、找数组中只出现一次的数字(其他都出现两次时,全部异或一遍结果就是那个单独的数)、简单加密。

左移和右移有什么坑?

左移 << 低位补 0,相当于乘以 2 的幂,没歧义。右移 >> 对无符号数高位补 0(逻辑移位),对有符号数高位补什么取决于编译器——可能补符号位(算术移位),也可能补 0。所以对有符号数做右移是未定义行为(实现定义),面试时说清楚这个区别。另外移位量不能超过类型的位宽,int x; x << 32 在 32 位 int 上是未定义行为。

位运算和乘除法哪个快?

现代编译器会自动把乘除 2 的幂优化成移位,手写 x >> 2x / 4 生成的汇编基本一样。所以别为了"性能"写移位代替除法——可读性更值钱。唯一需要手写位运算的场景是硬件寄存器操作、协议字段编解码,这时候用位运算是表达意图,不是优化性能。

位运算优先级有什么坑?

位运算优先级低于比较运算符。if (flags & 0x01 != 0) 实际解析为 if (flags & (0x01 != 0)),先算不等式再位与,结果完全不对。正确写法要加括号:if ((flags & 0x01) != 0)。这也是 C 语言最常见的一类 bug。

写段代码

c
// 打包两个 8 位值到 16 位 uint16_t pack(uint8_t hi, uint8_t lo) { return ((uint16_t)hi << 8) | lo; } // 解包 uint8_t hi = pack(0xCD, 0xAB) >> 8; // 0xCD uint8_t lo = pack(0xCD, 0xAB) & 0xFF; // 0xAB
标签:C语言