深入理解位操作
在计算机底层,所有数据都以二进制形式存储和运算。位操作作为直接操作二进制位的技术,具有高效、简洁的特点,广泛应用于底层开发、算法优化、嵌入式编程等领域。本文将从基础原理出发,结合实战代码,详细讲解位操作在乘法、除法、加法、平均值计算、绝对值获取及二进制 1 的个数统计等场景中的应用,帮助你掌握这一核心技术。
一、位操作基础:核心运算与原理
位操作基于二进制位(0 或 1)进行,核心运算包括与(&)、或(|)、异或(^)、左移(<<)、右移(>>),它们的规则如下:
| 运算符号 | 名称 | 规则说明 |
|---|---|---|
| & | 按位与 | 两位均为 1 时,结果为 1;否则为 0(可用于 “保留指定位”“判断奇偶”) |
| ^ | 按位异或 | 两位不同时,结果为 1;否则为 0(可用于 “无进位求和”“交换变量”“取反指定位”) |
| << | 左移 | 所有位向左移动 n 位,右侧补 0(等价于乘以 2ⁿ,效率远高于乘法运算符) |
| >> | 右移 | 正数左补 0,负数左补 1(等价于除以 2ⁿ,向下取整) |
位操作的核心优势在于直接操作硬件层面的数据,无需经过高级语言的运算优化,因此执行速度极快。例如,x << 3(乘以 8)的执行效率远高于x * 8,这也是底层开发中优先使用位操作的原因。
二、实战场景 1:快速实现乘法运算
乘法的本质是 “重复加法”,但直接循环加法效率较低。利用位操作的左移等价于乘 2特性,可大幅优化乘法实现,甚至支持正负数运算。
1. 快速求一个数的 7 倍
7 可以拆分为8 - 1,而 8 是 2³,因此 “乘以 7” 可转化为 “左移 3 位(乘 8)后减原数”,代码如下:
// 计算 x * 7
int multiply_by_7(int x) {
return (x << 3) - x; // 等价于 x*8 - x = x*7
}
该方法仅需 1 次左移和 1 次减法,执行效率远超x * 7。
2. 通用乘法实现(仅用逻辑运算)
若需实现任意两个整数的乘法(含负数),需先通过异或判断符号,再用 “左移 + 加法” 计算绝对值的乘积,最后调整符号。其中加法需用位操作实现(无+运算符)。
步骤拆解:
-
位操作实现加法:用异或(^)求 “无进位和”,用与(&)+ 左移求 “进位”,循环处理进位直至为 0;
-
处理符号:通过 “一正一负” 判断结果是否为负,用 “取反加 1”(补码特性)获取绝对值;
-
优化乘法:循环判断乘数的最低位,若为 1 则累加被乘数(左移后),同时乘数右移(等价于除以 2)。
完整代码:
// 仅用位操作实现加法(无+运算符)
int add(int a, int b) {
do {
int sum = a ^ b; // 异或:无进位求和
int carry = (a & b) << 1; // 与+左移:计算进位
a = sum;
b = carry;
} while (b != 0); // 进位为0时结束
return a;
}
// 仅用位操作实现乘法(支持正负数)
int multiply(int a, int b) {
int result = 0;
// 1. 判断结果符号(一正一负则为负)
int is_negative = ((a < 0) ^ (b < 0)) ? 1 : 0;
// 2. 取绝对值(补码特性:负数取反加1得绝对值)
a = (a < 0) ? add(~a, 1) : a;
b = (b < 0) ? add(~b, 1) : b;
// 3. 优化乘法:左移累加
while (b > 0) {
if (b & 1) { // 若b最低位为1,累加当前a(已左移)
result = add(result, a);
}
a <<= 1; // a左移:等价于a*2
b >>= 1; // b右移:等价于b/2,缩小循环范围
}
// 4. 调整符号(负数则取反加1)
return is_negative ? add(~result, 1) : result;
}
测试案例:
printf("3 * 5 = %d\n", multiply(3, 5)); // 输出15
printf("(-4) * 6 = %d\n", multiply(-4, 6)); // 输出-24
printf("(-2) * (-3) = %d\n", multiply(-2, -3)); // 输出6
三、实战场景 2:不用除法运算符实现除法
除法的本质是 “重复减法”,但直接循环减法效率极低(如 1000/1 需循环 1000 次)。利用位操作的右移等价于除 2特性,可通过 “二分法” 快速逼近商,大幅减少循环次数。
核心思路:
-
记录被除数的剩余部分(初始为被除数)和商(初始为 0);
-
每次找到最大的 2 的幂次倍数(
multi),使得 “除数 ×multi” 不超过剩余部分的一半; -
将
multi加到商中,同时从剩余部分中减去 “除数 ×multi”; -
重复步骤 2-3,直至剩余部分小于除数。
完整代码(仅处理正整数):
// 不用/运算符实现正整数除法(返回商的整数部分)
int div(const int x, const int y) {
int left_num = x; // 被除数剩余部分
int result = 0; // 商的结果
// 剩余部分 >= 除数时,继续计算
while (left_num >= y) {
int multi = 1; // 除数的倍数(初始为1,即2⁰)
// 找到最大的multi:y*multi <= 剩余部分的一半(left_num>>1等价于left_num/2)
while (add(y, y) <= left_num) { // 避免y*multi溢出,用add(y,y)替代y*2
multi <<= 1; // multi×2
y <<= 1; // 除数×2(减少乘法运算)
}
result = add(result, multi); // 商累加倍数
left_num -= y; // 剩余部分减去“除数×multi”
y >>= multi; // 恢复除数原值(因之前左移了multi次)
}
return result;
}
优化说明:
-
原思路中 “
y * multi” 可能导致溢出,此处用 “add(y, y)”(等价于y*2)替代,同时左移除数,避免溢出; -
循环次数从 “被除数大小” 降至 “二进制位数”(如 32 位整数最多循环 32 次),效率极大提升。
四、实战场景 3:其他高频位操作技巧
除了四则运算,位操作在 “平均值计算”“绝对值获取”“二进制 1 的个数统计” 等场景中也有巧妙应用,以下为具体实现。
1. 位操作求两个数的平均值
常规方法(a + b) / 2可能导致溢出(如a=2¹⁵-1、b=2¹⁵-1,相加后超出 int 范围),而位操作可避免溢出,核心原理基于二进制位的 “相同贡献” 和 “不同贡献”:
-
相同位(a&b):若两位均为 1,平均值为 1(
(1+1)/2=1),直接保留; -
不同位(a^b):若两位不同,和为 1,平均值为 0.5,右移 1 位(等价于除以 2)取整。
代码实现:
// 位操作求a和b的平均值(无溢出,向下取整)
int average(int a, int b) {
return (a & b) + ((a ^ b) >> 1);
}
示例验证:
-
a=3(011)、b=5(101):a&b=001,a^b=110,(110>>1)=11(3),结果1+3=4(正确); -
a=4(100)、b=5(101):a&b=100,a^b=001,(001>>1)=0,结果4+0=4(正确,(4+5)/2=4.5 向下取整为 4)。
2. 位操作计算数的绝对值
利用补码特性:负数的补码是 “原码除符号位取反加 1”,而绝对值的本质是 “符号位变为 0”。通过右移获取符号位,再用异或和加法实现通用绝对值计算:
-
正数:符号位为 0,右移 31 位(32 位 int)后为 0,异或后不变,最终结果为自身;
-
负数:符号位为 1,右移 31 位后为
0xFFFFFFFF(-1 的补码),异或后取反,加 1 后得到绝对值。
代码实现:
// 位操作计算绝对值(支持32位int)
int abs(int x) {
int sign = x >> 31; // 获取符号位(正数0,负数0xFFFFFFFF)
return (x ^ sign) - sign; // 正数:(x^0)-0=x;负数:(x取反)-(-1)=x取反+1
}
示例验证:
-
x=5(0x00000005):sign=0,(5^0)-0=5(正确); -
x=-5(0xFFFFFFFB):sign=0xFFFFFFFF,(FB ^ FF)=04,04 - FF=05(正确)。
3. 统计二进制中 1 的个数
核心技巧:x & (x-1) 会 “消除 x 的最后一个 1”(如x=6(110),x-1=5(101),x&(x-1)=100,消除了最后一个 1)。循环执行该操作,直至 x 为 0,循环次数即为 1 的个数。
代码实现:
// 统计x的二进制中1的个数
int count_one(int x) {
int count = 0;
while (x) {
count++;
x = x & (x - 1); // 消除最后一个1
}
return count;
}
示例验证:
-
x=7(111):循环 3 次(111→110→100→0),count=3(正确); -
x=8(1000):循环 1 次(1000→0),count=1(正确)。
五、总结:位操作的核心价值与应用场景
位操作的本质是 “直接操作二进制位”,其核心价值在于高效性(避免高级运算的开销)和安全性(避免溢出)。常见应用场景包括:
-
底层开发:驱动程序、嵌入式系统中操作硬件寄存器(如控制 GPIO 引脚、配置定时器);
-
算法优化:位排序、布隆过滤器、状态压缩(如 DP 中的状态用二进制表示);
-
数据处理:哈希算法、加密算法(如 AES、RSA 中大量使用位操作);
-
性能敏感场景:游戏引擎、实时系统中需要快速运算的模块。
掌握位操作不仅能提升代码效率,更能帮助你理解计算机底层的运算逻辑,是进阶高级开发工程师的必备技能。建议结合本文代码反复实践,逐步积累实战经验。
openvela 操作系统专为 AIoT 领域量身定制,以轻量化、标准兼容、安全性和高度可扩展性为核心特点。openvela 以其卓越的技术优势,已成为众多物联网设备和 AI 硬件的技术首选,涵盖了智能手表、运动手环、智能音箱、耳机、智能家居设备以及机器人等多个领域。
更多推荐

所有评论(0)