位运算符
1. 按位与运算符 (&)
按位与运算符对两个整数的每一位进行“与”操作,只有当两个位都是1时,结果才为1,否则为0。
int a = 5; // 二进制: 00000101
int b = 3; // 二进制: 00000011
int result = a & b; // 结果: 00000001 (1)
2. 按位或运算符 (|)
按位或运算符对两个整数的每一位进行“或”操作,只要有一个位是1,结果就为1,否则为0。
int a = 5; // 二进制: 00000101
int b = 3; // 二进制: 00000011
int result = a | b; // 结果: 00000111 (7)
3. 按位异或运算符 (^)
按位异或运算符对两个整数的每一位进行“异或”操作,当两个位不同,结果为1,相同时为0。
int a = 5; // 二进制: 00000101
int b = 3; // 二进制: 00000011
int result = a ^ b; // 结果: 00000110 (6)
4. 取反运算符 (~)
取反运算符对整数的每一位进行“取反”操作,0变1,1变0。
int a = 5; // 二进制: 00000101
int result = ~a; // 结果: 11111010 (对于32位整数,结果为-6)
在有符号整数中,按位取反是针对补码进行操作的,因此取反后的结果表示的是该数的补码形式。
5. 左移运算符 (<<)
左移运算符将一个整数的二进制表示向左移动指定的位数,右侧用0填充,左侧的位丢弃。
int a = 5; // 二进制: 00000101
int result = a << 1; // 结果: 00001010 (10)
6. 右移运算符 (>>)
右移运算符将一个整数的二进制表示向右移动指定的位数,左侧用符号位填充(对于有符号整数),右侧的位丢弃。
int a = 5; // 二进制: 00000101
int result = a >> 1; // 结果: 00000010 (2)
复杂的位运算举例
交换两个整数而不使用临时变量
int x = 5; // 二进制: 00000101
int y = 3; // 二进制: 00000011
x = x ^ y; // x 变为 00000110 (6)
y = x ^ y; // y 变为 00000101 (5)
x = x ^ y; // x 变为 00000011 (3)
// 最终 x = 3, y = 5
判断一个整数是否为2的幂
int n = 16; // 二进制: 00010000
bool isPowerOfTwo = (n & (n - 1)) == 0; // true,因为16是2的幂
位段(Bit Fields)
位段是C语言结构体的一部分,可以指定成员变量占用的位数。在嵌入式系统或需要节省内存的应用中非常有用。
定义位段
#include <stdio.h>
struct {
unsigned int a : 1; // 占1位
unsigned int b : 3; // 占3位
unsigned int c : 4; // 占4位
} bitFields;
int main() {
bitFields.a = 1; // 设置位段a为1
bitFields.b = 5; // 设置位段b为5(二进制: 101)
bitFields.c = 9; // 设置位段c为9(超出范围,结果取最低4位:二进制: 1001)
printf("a: %u, b: %u, c: %u\n", bitFields.a, bitFields.b, bitFields.c);
return 0;
}
在这个例子中,bitFields结构体包含三个位段:
- a 占用1位,范围是0到1。
- b 占用3位,范围是0到7。
- c 占用4位,范围是0到15。
位段的使用可以显著节省内存,但也带来了一些潜在的问题:
1. 位段的内存布局和对齐方式依赖于具体的编译器和平台。
2. 位段的访问和操作可能比普通整数操作稍慢。