一、基本位运算符
1. 按位与(&
)
-
规则:两位都为1时结果为1,否则为0。
-
用途:提取特定位、掩码操作。
-
示例:
unsigned char a = 0b1010; // 10
unsigned char b = 0b1100; // 12
unsigned char c = a & b; // 0b1000 → 8
2. 按位或(|
)
-
规则:两位中有一个为1则结果为1。
-
用途:设置特定位。
-
示例:
unsigned char a = 0b1010; // 10
unsigned char b = 0b1100; // 12
unsigned char c = a | b; // 0b1110 → 14
3. 按位异或(^
)
-
规则:两位不同时结果为1,否则为0。
-
用途:切换特定位、交换变量。
-
示例:
unsigned char a = 0b1010; // 10
unsigned char b = 0b1100; // 12
unsigned char c = a ^ b; // 0b0110 → 6
4. 按位取反(~
)
-
规则:每一位取反(0变1,1变0)。
-
用途:反转所有位。
-
示例:
unsigned char a = 0b1010; // 10
unsigned char c = ~a; // 0b0101 → 5(实际为255-10=245,但需注意类型宽度)
二、移位运算符
1. 左移(<<
)
-
规则:将二进制位向左移动指定位数,右侧补0。
-
用途:快速乘以2的幂。
-
示例:
unsigned char a = 0b00001010; // 10
unsigned char c = a << 2; // 0b00101000 → 40(10*2²=40)
2. 右移(>>
)
-
规则:
-
无符号数(
unsigned
):左侧补0。 -
有符号数(
signed
):左侧补符号位(称为算术右移)。
-
-
用途:快速除以2的幂(仅对无符号数安全)。
-
示例:
unsigned char a = 0b10100000; // 160
char b = -8; // 二进制补码:11111000
unsigned char c = a >> 2; // 0b00101000 → 40(160/2²=40)
char d = b >> 2; // 11111110 → -2(保留符号位)
三、关键概念:无符号数 vs. 有符号数
-
无符号数(
unsigned
):所有位都表示数值(范围:0 ~ 2ⁿ-1)。 -
有符号数(
signed
):最高位为符号位(0正1负),范围:-2ⁿ⁻¹ ~ 2ⁿ⁻¹-1。 -
移位时的区别:
-
右移无符号数:逻辑右移(左侧补0)。
-
右移有符号数:算术右移(左侧补符号位)。
-
四、常见应用场景
- 掩码操作(提取或设置特定位):
// 提取低4位
unsigned char data = 0xAB; // 1010 1011
unsigned char low = data & 0x0F; // 0x0B (0000 1011)
- 位标志管理(如权限控制):
#define READ 0b00000001
#define WRITE 0b00000010
#define EXECUTE 0b00000100
unsigned char perm = READ | WRITE; // 设置读写权限
if (perm & READ) { ... } // 检查读权限
- 快速乘除(仅限2的幂):
int a = 10;
int b = a << 3; // 10 * 8 = 80
int c = a >> 1; // 10 / 2 = 5
- 交换变量(无需临时变量):
int x = 5, y = 3;
x ^= y; // x = 5 ^ 3
y ^= x; // y = 3 ^ (5 ^ 3) = 5
x ^= y; // x = (5 ^ 3) ^ 5 = 3
五、注意事项
-
移位溢出:
-
左移超过类型位数是未定义行为(如
int32_t a = 1; a << 32
)。
-
-
负数右移:
-
对有符号数右移可能导致符号扩展(如
-8 >> 1
得到-4
)。
-
-
优先级问题:
-
位运算符优先级低于算术运算符,建议用括号明确优先级:
-
int a = 5 & 3 + 2; // 等价于 5 & (3 + 2) → 5 & 5 = 5
int b = (5 & 3) + 2; // (1) + 2 = 3