位操作:底层编程利器
目录
一、位操作符概述
二、基本位操作符
1、按位与(&)
2、按位或(|)
3、按位异或(^)
核心特性
1. 交换律
2. 结合律
3. 自反性(同一性)
4. 与0的关系
5. 可逆性
4、按位取反(~)
5、左移(<<)
6、右移(>>)
三、基本位操作示例
四、位操作应用:不借助临时变量交换两个整数
五、练习1:计算整数二进制表示中1的个数
方法1:除2取余法(有缺陷)
方法2:移位检测法
方法3:Brian Kernighan算法(最优)(超重要!!!)
六、练习2:二进制位操作 - 置位与清零
位操作技巧总结:
七、位操作符的应用
1、设置特定位
2、清除特定位
3、切换特定位
4、检查特定位
5、交换两个变量的值(不使用临时变量)
6、计算奇偶性
八、注意事项
一、位操作符概述
位操作符是C语言中用于对数据的二进制位进行操作的运算符。它们直接操作整数类型的位模式,在底层编程、硬件操作和性能优化中非常有用。
位操作符和逻辑运算符几乎一样的作用,只不过不同点是:位操作符是针对具体某一位的操作,而逻辑运算符是针对某一个具体逻辑的操作。可以类推......
C语言提供了以下几种位操作符,它们只能用于整数类型:
-
&
- 按位与 (AND) -
|
- 按位或 (OR) -
^
- 按位异或 (XOR) -
~
- 按位取反 (NOT)
二、基本位操作符
1、按位与(&)
-
功能:对两个操作数的每一位执行逻辑与操作
-
规则:只有两个对应位都为1时,结果位才为1
-
示例:
unsigned int a = 5; // 0101 unsigned int b = 3; // 0011 unsigned int c = a & b; // 0001 (1)
2、按位或(|)
-
功能:对两个操作数的每一位执行逻辑或操作
-
规则:只要有一个对应位为1,结果位就为1
-
示例:
unsigned int a = 5; // 0101 unsigned int b = 3; // 0011 unsigned int c = a | b; // 0111 (7)
3、按位异或(^)
-
功能:对两个操作数的每一位执行异或操作
-
规则:对应位不同时结果为1,相同时为0
-
示例:
unsigned int a = 5; // 0101 unsigned int b = 3; // 0011 unsigned int c = a ^ b; // 0110 (6)
核心特性
1. 交换律
a ^ b == b ^ a
2. 结合律
(a ^ b) ^ c == a ^ (b ^ c)
3. 自反性(同一性)
a ^ a == 0
任何数与自身异或结果为0
4. 与0的关系
a ^ 0 == a
任何数与0异或结果为其本身
5. 可逆性
a ^ b ^ b == a
异或同一个数两次会得到原数
4、按位取反(~)
-
功能:对操作数的每一位执行取反操作
-
规则:1变0,0变1
-
示例:
unsigned int a = 5; // 0101 unsigned int b = ~a; // 1010 (取决于int的位数,假设4位时为10)
5、左移(<<)
-
功能:将操作数的所有位向左移动指定的位数
-
规则:右边空出的位用0填充,左边溢出的位被丢弃
-
示例:
unsigned int a = 5; // 0101 unsigned int b = a << 2; // 010100 (20)
6、右移(>>)
-
功能:将操作数的所有位向右移动指定的位数
-
规则:对于无符号数,左边空出的位用0填充;对于有符号数,取决于实现(通常用符号位填充)
-
示例:
unsigned int a = 20; // 010100 unsigned int b = a >> 2; // 000101 (5)
三、基本位操作示例
#include <stdio.h>int main()
{int num1 = -3;int num2 = 5;printf("%d\n", num1 & num2); // 按位与printf("%d\n", num1 | num2); // 按位或printf("%d\n", num1 ^ num2); // 按位异或printf("%d\n", ~0); // 按位取反return 0;
}
四、位操作应用:不借助临时变量交换两个整数
#include <stdio.h>int main()
{int a = 10;int b = 20;a = a ^ b;b = a ^ b;a = a ^ b;printf("a = %d b = %d\n", a, b);return 0;
}
五、练习1:计算整数二进制表示中1的个数
方法1:除2取余法(有缺陷)
#include <stdio.h>int main()
{int num = 10;int count = 0;while(num){if(num % 2 == 1)count++;num = num / 2;}printf("二进制中1的个数 = %d\n", count);return 0;
}
缺陷:对于负数会出错,因为负数的除法行为与预期不同。
方法2:移位检测法
#include <stdio.h>int main()
{int num = -1;int count = 0;for(int i = 0; i < 32; i++){if(num & (1 << i))count++;}printf("二进制中1的个数 = %d\n", count);return 0;
}
特点:需要循环32次,适用于所有整数,但效率不高。
方法3:Brian Kernighan算法(最优)(超重要!!!)
#include <stdio.h>int main()
{int num = -1;int count = 0;while(num){count++;num = num & (num - 1); // 清除最右边的1}printf("二进制中1的个数 = %d\n", count);return 0;
}
优点:循环次数等于1的个数,效率最高。
六、练习2:二进制位操作 - 置位与清零
将数字13的第5位(从0开始计数)修改为1,然后再改回0:
#include <stdio.h>int main()
{int a = 13;// 13的二进制: 00000000000000000000000000001101// 将第5位置为1a = a | (1 << 4); // 1左移4位到第5位printf("a = %d\n", a); // 输出29 (000...00011101)// 将第5位清零a = a & ~(1 << 4);printf("a = %d\n", a); // 输出13 (000...00001101)return 0;
}
位操作技巧总结:
-
将某位置1:
num | (1 << n)
-
将某位清零:
num & ~(1 << n)
-
切换某位状态:
num ^ (1 << n)
-
检查某位是否为1:
num & (1 << n)
这些位操作在底层编程、嵌入式系统和性能优化中非常有用。
七、位操作符的应用
1、设置特定位
// 设置第n位(从0开始计数)
num |= (1 << n);
2、清除特定位
// 清除第n位
num &= ~(1 << n);
3、切换特定位
// 切换第n位的状态
num ^= (1 << n);
4、检查特定位
// 检查第n位是否设置
if (num & (1 << n)) {// 位已设置
}
5、交换两个变量的值(不使用临时变量)
a ^= b;
b ^= a;
a ^= b;
6、计算奇偶性
int is_odd = num & 1; // 如果为1则是奇数,0则是偶数
八、注意事项
-
移位操作的位数:不能超过或等于操作数的位数,否则行为未定义
-
有符号数的右移:结果依赖于实现(算术移位或逻辑移位)
-
位操作符的优先级:通常低于算术运算符,建议使用括号明确优先级
-
可移植性:某些位操作假设特定的整数表示(如补码),可能影响可移植性
位操作是C语言强大功能之一,合理使用可以显著提高程序效率和减少内存使用,但也需要谨慎以避免难以发现的错误。