位运算直接对整数在内存中的二进制位进行操作,是C语言中高效处理数据的强大工具。在Linux系统编程中,位运算常用于设备驱动、系统调用、权限管理等场景。
按位与(&): 两个操作数的对应位都为1时,结果的该位才为1
unsigned int a = 5; // 0101
unsigned int b = 3; // 0011
unsigned int c = a & b; // 0001 (1)
按位或(|): 两个操作数的对应位有一个为1时,结果的该位就为1
unsigned int d = a | b; // 0111 (7)
按位异或(^): 两个操作数的对应位不同时,结果的该位为1
unsigned int e = a ^ b; // 0110 (6)
按位取反(~): 对操作数的每一位取反
unsigned int f = ~a; // 1111...1010 (取决于int大小)
左移(<<): 将操作数的所有位向左移动指定的位数,右侧补0
unsigned int g = a << 1; // 1010 (10)
右移(>>): 将操作数的所有位向右移动指定的位数
unsigned int h = a >> 1; // 0010 (2)
Linux文件权限使用位掩码表示:
#define S_IRUSR 0400 // 用户读权限
#define S_IWUSR 0200 // 用户写权限
#define S_IXUSR 0100 // 用户执行权限
// 类似的有组和其他用户的权限
// 设置权限
mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; // 0644
// 检查权限
if (mode & S_IRUSR) {
// 用户有读权限
}
Linux系统调用和库函数常用标志位:
// 打开文件时的标志
int flags = O_RDWR | O_CREAT | O_TRUNC;
// 检查是否设置了某个标志
if (flags & O_CREAT) {
// 如果文件不存在则创建
}
设备驱动中常用位域表示寄存器:
struct device_status {
unsigned int ready:1; // 1位表示设备就绪状态
unsigned int error:1; // 1位表示错误状态
unsigned int reserved:30; // 保留位
};
unsigned int set_bit(unsigned int num, int pos) {
return num | (1 << pos);
}
unsigned int clear_bit(unsigned int num, int pos) {
return num & ~(1 << pos);
}
unsigned int toggle_bit(unsigned int num, int pos) {
return num ^ (1 << pos);
}
int check_bit(unsigned int num, int pos) {
return (num >> pos) & 1;
}
int hamming_weight(unsigned int n) {
int count = 0;
while (n) {
count += n & 1;
n >>= 1;
}
return count;
}
位运算与算术运算: 位运算通常比算术运算更快,但现代编译器已经能很好优化简单算术运算。
移位与乘除法: 在性能敏感代码中,用移位代替乘除2的幂次方运算
x *= 8; // 可以替换为
x <<= 3;
缓存友好性: 使用位压缩数据可以减少内存占用,提高缓存命中率。
内核中提供了丰富的位操作API,如:
// 在include/linux/bitops.h中
set_bit(int nr, volatile unsigned long *addr);
clear_bit(int nr, volatile unsigned long *addr);
test_bit(int nr, volatile unsigned long *addr);
TCP头部标志使用位域:
struct tcphdr {
// ...
u_int16_t fin:1, syn:1, rst:1, psh:1, ack:1, urg:1, ece:1, cwr:1;
// ...
};
#define FLAG_A 0x01
#define FLAG_B 0x02
#define FLAG_C 0x04
unsigned char flags = 0;
// 设置标志
flags |= FLAG_A;
flags |= FLAG_C;
// 清除标志
flags &= ~FLAG_B;
// 检查标志
if (flags & FLAG_A) {
// FLAG_A被设置
}
符号位问题: 对有符号整数进行右移时,结果取决于实现(算术右移或逻辑右移)
移位溢出: 移位位数超过类型宽度是未定义行为
可读性: 复杂的位操作应添加充分注释
可移植性: 不同架构可能有不同的字节序(大端/小端)
位运算是Linux系统编程中的重要工具,合理使用可以写出高效、紧凑的代码,特别适合底层硬件操作和性能关键代码。