在嵌入式开发中,外设(GPIO、UART、ADC、定时器等)通常通过内存映射的寄存器控制。这些寄存器的每一位或几位代表特定功能(如使能、方向、中断标志等),不用位运算,就无法精确控制某一位而不影响其他位。
例如:从一个 16 位传感器数据中提取高 4 位作为类型,低 12 位作为值:
cpp
uint16_t data = read_sensor();
uint8_t type = (data >> 12) & 0x0F; // 高4位
uint16_t value = data & 0x0FFF; // 低12位
又或者,设置 GPIOA 的第 5 位为输出高电平
cpp
GPIOA->ODR |= (1 << 5); // 置位:输出高
GPIOA->ODR &= ~(1 << 5); // 清零:输出低
C/C++ 中常用的位运算符:
| 运算符 | 名称 | 说明 | 示例(a=5, b=3) |
|---|---|---|---|
& |
按位与 | 对应位都为1,结果为1 | 5 & 3 = 1 (0101 & 0011 = 0001) |
| ` | ` | 按位或 | 对应位有一个为1,结果为1 |
^ |
按位异或 | 对应位不同为1 | 5 ^ 3 = 6 (0101 ^ 0011 = 0110) |
~ |
按位取反 | 0变1,1变0 | ~5(假设8位)= 11111010 = -6(补码) |
<< |
左移 | 各位左移,低位补0 | 5 << 1 = 10 (0101 → 1010) |
>> |
右移 | 逻辑右移(无符号)或算术右移(有符号) | 5 >> 1 = 2 (0101 → 0010) |
常用位运算技巧
置位(Set bit):reg |= (1 << n); // 将 reg 的第 n 位置 1
清零(Clear bit):reg &= ~(1 << n); // 将 reg 的第 n 位清 0
翻转(Toggle bit):reg ^= (1 << n); // 第 n 位 0→1,1→0
判断某位是否为1:if (reg & (1 << n)) { }
提取连续几位:uint8_t field = (reg >> 2) & 0x07; // 0x07 = 0b111,提取 bits [4:2](共3位)
力扣中对应题目
二进制枚举类问题:78. 子集 - 力扣(LeetCode)
cpp
class Solution {
public:
// 位运算,从0000-1111一直遍历所有情况
vector<vector<int>> subsets(vector<int>& a) {
vector<vector<int>> result;
int n = a.size();
int maxstate = 1 << n;
for(int s = 0; s < maxstate; s++) {
vector<int> tmp;
for(int i = 0; i < n; i++) {
int b = (s >> i) & 1; // 提取s的第i位
if(b) tmp.push_back(a[i]);
}
result.push_back(tmp);
}
return result;
}
};
lowbit类问题:191. 位1的个数 - 力扣(LeetCode)
cpp
class Solution {
public:
// 常规方法
// int hammingWeight(int n) {
// int cnt = 0;
// for(int i = 0; i < 32; i++) {
// if((n >> i) & 1) cnt++;
// }
// return cnt;
// }
// lowbit 方法
int hammingWeight(int n) {
int cnt = 0;
while(n) {
int lowbit = n & (-n);
cnt++;
n -= lowbit;
}
return cnt;
}
};
数位问题:137. 只出现一次的数字 II - 力扣(LeetCode)
cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int res = 0;
for(int i = 0; i < 32; i++) {
int cnt = 0;
for(int x : nums) {
int bit = (x >> i) & 1;
if(bit) cnt++;
}
if(cnt % 3) res |= 1 << i;
}
return res;
}
};
cpp
class Solution {
public:
int rangeBitwiseAnd(int left, int right) {
int ret = 0;
for(int i = 30; i >= 0; i--) { // 从最高位开始遍历
int bl = (left >> i) & 1; // l的第i位
int br = (right >> i) & 1; // 第i位
if(bl && br) ret |= 1 << i; // 1 1 - res这位也为1
else if(!bl && br) break; // 0 1 - 这位后面都是0
}
return ret;
}
};
异或技巧类问题:136. 只出现一次的数字 - 力扣(LeetCode)
cpp
class Solution {
public:
int singleNumber(vector<int>& nums) {
int result = 0;
for(int i = 0; i < nums.size(); i++){
result ^= nums[i];
}
return result;
}
};
上面五道题是位运算基础题,也是笔面试常考题,更深入的位运算题目可自行了解。