1. 按位与 &
运算规则
0 & 0 = 0
0 & 1 = 0
1 & 0 = 0
1 & 1 = 1特点
- 有0出0,全1出1
- 常用于”清零”、“提取”某些位
示例
int a = 0b1100; // 12
int b = 0b1010; // 10
int c = a & b; // 0b1000 = 8
/*
1100
& 1010
----
1000
*/※ 有关 a 和 b 变量中数值存储的方式以及运算的具体过程,参见数值的存储法与运算法。
常见应用
① 判断奇偶
if (n & 1) { // 1 = 0b0000 0000 ... 0001,因此 n&1的值 = n二进制最低位
printf("奇数\n"); // 二进制最低位为1,则n = 某正整数*2+1 => 偶数+1 => 奇数
} else {
printf("偶数\n"); // 二进制最低位为0,则n = 某正整数*2 => 偶数
}② 提取特定位
int num = 0b1010 1100;
int low_4bits = num & 0x0F; // 提取低4位:0b1100 (0x0F = 0b0000 1111)
int high_4bits = num & 0xF0; // 提取高4位:0b1010 0000 (0xF0 = 0b1111 0000)③ 清零特定位
int num = 0b1111 1111;
int clear_low_4 = num & 0xF0; // 清零低4位:0xF0 = 0b1111 0000④ 判断是否为 2 的幂
bool is_power_of_2(int n) {
return n > 0 && (n & (n - 1)) == 0;
}
// 8 (1000) & 7 (0111) = 0,是2的幂
// 6 (0110) & 5 (0101) = 0100,不是2的幂※ 为什么可以这样写?参见快速判断n是否为2的幂。
2. 按位或 |
运算规则
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1特点
- 有1出1,全0出0
- 常用于”置位”操作
示例
int a = 0b1100; // 12
int b = 0b1010; // 10
int c = a | b; // 0b1110 = 14
/*
1100
| 1010
----
1110
*/常见应用
① 设置特定位为 1
int num = 0b1010 0000;
int set_bit_2 = num | 0b0000 0100; // 设置第2位为1:0b1010 0100② 合并标志位(常用)
#define FLAG_A 0x01 // 0001
#define FLAG_B 0x02 // 0010
#define FLAG_C 0x04 // 0100
#define FLAG_D 0x08 // 1000
int flags = 0;
flags |= FLAG_A; // 启用A:0001
flags |= FLAG_C; // 启用C:0101
// 同时启用了 A 和 C3. 按位异或 ^
运算规则
0 ^ 0 = 0
0 ^ 1 = 1
1 ^ 0 = 1
1 ^ 1 = 0特点
- 相同得 0,不同得 1
- 常用于”翻转”、“加密”
示例
int a = 0b1100; // 12
int b = 0b1010; // 10
int c = a ^ b; // 0b0110 = 6
/*
1100
^ 1010
----
0110
*/常见应用
① 翻转特定位(巧妙)
int num = 0b1010 0000;
int toggle_bit_4 = num ^ 0b0001 0000; // 翻转第4位:0b1011 0000| 异或数位 | 原数位 | 结果 |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 1 |
| 1 | 0 | 1 |
| 1 | 1 | 0 |
结论:异或数位为0 => 原数不变;异或数位为1 => 原数取反。
② 不借助临时变量交换两个数(巧妙)
原理:【与加密原理相同】与同一数异或两次等于没有异或。
int a = 5, b = 9;
a ^= b; // a = 5^9
b ^= a; // b = 9^(5^9) = 5
a ^= b; // a = (5^9)^5 = 9
// 现在 a=9, b=5③ 简单加密
char data = 'A'; // 0100 0001
char key = 0x55; // 0101 0101
char encrypted = data ^ key; // 0001 0100
char decrypted = encrypted ^ key; // 0100 0001 = 'A'④ 找出唯一的奇数次数数字
// 数组中只有一个数字出现奇数次,其他都是偶数次
int find_odd(int arr[], int n) {
int result = 0;
for (int i = 0; i < n; i++) {
result ^= arr[i];
}
return result;
}
// [1,2,3,2,1] → 1^2^3^2^1 = 34. 按位取反 ~
运算规则
~0 = 1
~1 = 0特点
- 一元运算符,将所有位取反
- 包括符号位,可能将整数翻转为负数
示例
int a = 0b0000 1100; // 12
int b = ~a; // 0b1111 0011 = -13 (补码表示)常见应用
① 快速计算相反数(巧妙)
int x = 5;
int neg_x = ~x + 1; // -5
// 计算机用补码表示负数※ 为什么?参见快速取相反数算法的数学原理。
② 生成掩码
int mask = ~0; // 所有位为1:0xFFFFFFFF
int low_byte_mask = ~0xFF; // 高24位为1,低8位为0(假设为64位系统,int变量占4个字节)5. 左移 <<
运算规则
- 所有位向左移动,右边补 0
- 对移出的高位部分的处理方式为未定义行为
- 每左移 1 位,相当于乘以 2
示例
int a = 0b0000 1100; // 12
int b = a << 1; // 0b0001 1000 = 24
int c = a << 2; // 0b0011 0000 = 48
int d = a << 4; // 0b1100 0000 = 192常见应用
① 快速乘法
int x = 5;
int result1 = x << 1; // 10 (5×2)
int result2 = x << 3; // 40 (5×8)
int result3 = x << 4; // 80 (5×16)② 创建掩码
int bit_3 = 1 << 3; // 0b00001000 (第3位为1)
int bits_0_3 = (1 << 4) - 1; // 0b00001111 (低4位全为1)③ 组合标志位
#define BIT0 (1 << 0) // 0001
#define BIT1 (1 << 1) // 0010
#define BIT2 (1 << 2) // 0100
#define BIT3 (1 << 3) // 10006. 右移 >>
运算规则
- 所有位向右移动,丢弃移出的低位部分
- 左边补位取决于类型:
- 无符号数:补 0(逻辑右移)
- 有符号数:补原符号位(算术右移,保证“得数为原数除以2后向下取整”的数学关系成立)
※ 为什么?参见有符号整型数算术右移相关变化的数学原理。
示例
// 正数右移(补0,逻辑右移)
int a = 0b1100 0000; // 192
int b = a >> 1; // 0b0110 0000 = 96
int c = a >> 2; // 0b0011 0000 = 48
// 负数右移(补1,算术右移)
int d = -8; // 0b1111 1000 (补码)
int e = d >> 1; // 0b1111 1100 = -4
int f = d >> 2; // 0b1111 1110 = -2常见应用
① 快速除法(2的幂)
int x = 100;
int result1 = x >> 1; // 50 (100÷2)
int result2 = x >> 2; // 25 (100÷4)
int result3 = x >> 3; // 12 (100÷8,向下取整)② 提取高位
int num = 0b1010 1100;
int high_4 = num >> 4; // 0b0000 1010
// 64位系统中,int类型变量占4byte=32bit,故第7位(从第0位开始)并非最高位7. 复合赋值运算符
a &= b; // 等价于 a = a & b
a |= b; // 等价于 a = a | b
a ^= b; // 等价于 a = a ^ b
a <<= n; // 等价于 a = a << n
a >>= n; // 等价于 a = a >> n8. 运算符优先级
从高到低:
1. ~ // 按位取反
2. << >> // 移位
3. & // 按位与
4. ^ // 按位异或
5. | // 按位或
6. &= ^= |= <<= >>= // 复合赋值注意:位运算符优先级低于算术运算符,建议使用括号明确意图。
9. 综合应用实例
① 位字段管理
// 定义一个8位状态寄存器
unsigned char status = 0;
// 设置第3位(从0开始)
status |= (1 << 3);
// 清除第3位
status &= ~(1 << 3);
// 切换第3位
status ^= (1 << 3);
// 检查第3位
if (status & (1 << 3)) {
printf("第3位是1\n");
}② RGB颜色处理
// 将RGB打包成32位整数
unsigned int rgb = (r << 16) | (g << 8) | b;
// 提取各颜色分量
int r = (rgb >> 16) & 0xFF; // 0xFF=0b1111 1111
int g = (rgb >> 8) & 0xFF;
int b = rgb & 0xFF;③ 权限系统(巧妙,重要)
#define PERM_READ (1 << 0) // 0001 = 1
#define PERM_WRITE (1 << 1) // 0010 = 2
#define PERM_EXECUTE (1 << 2) // 0100 = 4
#define PERM_DELETE (1 << 3) // 1000 = 8
int permissions = 0;
// 授予读写权限
permissions |= PERM_READ | PERM_WRITE; // 0011 = 3
// 检查是否有读权限
if (permissions & PERM_READ) {
printf("有读权限\n");
}
// 撤销写权限
permissions &= ~PERM_WRITE; // 0001 = 1④ 高效计算
// 计算2的n次方(n < 31)
int power_of_2 = 1 << n;
// 判断奇偶(odd adj.奇数的)
bool is_odd = n & 1;