c语言中使用(>> )运算替代(/ %)运算实现优化
在 C 语言中,使用位运算 >>(右移)和 &(按位与)来代替除法 / 和取模 % 运算,主要是因为位运算在某些情况下比算术运算更高效。具体原因如下:
1. 位运算速度更快
- 右移
>>相当于除以 2 的幂,例如x >> 1相当于x / 2,x >> 2相当于x / 4,以此类推。 - 按位与
&操作可以用于取模运算,但只能用于模 2 的幂的情况。例如x % 32可以使用x & 0x1F来替代,x % 4可以使用x & 0x03。 - 在大多数处理器架构中,除法和取模运算通常比位移和按位与运算要慢。因为除法涉及更复杂的硬件操作,而位运算直接操作二进制位,硬件执行起来更快。
2. 位运算的常见优化
- 除以 2 的幂次方: 用右移
>>代替除法/。例如,x / 8等价于x >> 3。 - 取模 2 的幂次方: 用按位与
&代替取模%。例如,x % 32等价于x & 31。
这种优化在嵌入式编程、驱动程序开发和操作系统内核代码中尤其常见,因为这些领域对性能有很高的要求。
3. 代码示例
extern __inline__ int test_bit(int nr, const unsigned long * addr)
{int mask;addr += nr >> 5;mask = 1 << (nr & 0x1f);return ((mask & *addr) != 0);
}
test_bit 函数用于检测位数组中某一位是否被设置(即为1)。
实现步骤
-
确定要访问的
unsigned long元素:addr += nr >> 5;- 这里的
nr >> 5实际上是将nr右移5位,这相当于nr / 32。因为一个unsigned long通常占 4 字节(32 位),所以右移5位就可以确定nr所在的unsigned long元素的索引位置。 - 例如,如果
nr是 35,那么nr >> 5等于 1,这意味着你要访问位数组中的第二个unsigned long元素(因为数组索引从0开始)。
- 这里的
-
计算要测试的位的掩码:
mask = 1 << (nr & 0x1f);nr & 0x1f是对nr取低5位,这相当于nr % 32,即计算nr在当前unsigned long元素中的位置。1 << (nr & 0x1f)是将数字1左移到nr在当前unsigned long元素中对应的位置。例如,如果nr & 0x1f结果为 3,则mask = 1 << 3 = 0b1000,即第4位是1。
-
进行位测试:
return ((mask & *addr) != 0);*addr取当前的unsigned long元素的值。- 然后通过
mask & *addr来检查mask对应的那一位是否在*addr中被设置为1。 - 如果
mask对应的位为1,则返回1,否则返回0。
4. 优点总结
- 效率高:位移和按位与的计算通常只需要一个 CPU 指令,而除法和取模则可能需要多个指令甚至硬件支持,因此性能差异明显。
- 简洁:位运算在处理固定大小的块或需要快速循环/分配资源时更加简洁和易于控制。
5. 注意事项
- 适用范围有限:位运算优化只适用于 2 的幂次方情况。例如,如果除数或模数不是 2 的幂,不能直接使用
>>和&来代替。 - 代码可读性:尽管位运算更高效,但过多使用位运算可能降低代码的可读性,尤其对于不熟悉位运算的人来说。
6. 硬件层面支持
- 在硬件层面,乘法、除法往往需要额外的硬件模块来支持,其耗费的时钟周期较多。相比之下,移位和按位操作都是简单的逻辑运算,现代处理器能够在一个时钟周期内完成。
