位运算是指按二进制进行的运算,直接操作二进制的位,操作效率较高。在系统软件中,常常需要处理二进制位的问题。C语言提供了6个位操作运算符。这些运算符只能用于整型操作数,即只能用于带符号或无符号的char,short,int与long类型。
1.位运算符与 &
& 按位与,对应的两个二进制位均为1时,结果才为1,否则为0
9 & 5 = 1
9 对应二进制 --> 1001
5 对应二进制 --> 0101
1001
&0101
----------
0001 // 转为十进制为1
2.位运算符或 |
| 按位或,对应的两个二进制位有一个位1,结果就为1,否则为0
9 | 5 = 13
9 对应二进制 --> 1001
5 对应二进制 --> 0101
1001
|0101
----------
1101 // 转为十进制为13
3.位运算符异或
^按位异或,对应的两个二进制位不相同时,结果为1,否则为0
9 ^ 5 = 12
9 对应二进制 --> 1001
5 对应二进制 --> 0101
1001
^0101
----------
1100 // 转为十进制为12
4.位运算符取反
~取反,对于二进制位取反(0变1,1变0)
~9 = -10
~5 = -8
9 对应二进制补码 --> 0000 0000 0000 0000 0000 0000 0000 1001
先将补码取反
~0000 0000 0000 0000 0000 0000 0000 1001
------------------------------------------------------------------
1111 1111 1111 1111 1111 1111 1111 0110 // 得到取反后的补码
然后将补码转为反码,由于二进制首位是1,表示为负数,则需要先转为反码(在补码的基础上减1)
1111 1111 1111 1111 1111 1111 1111 0101 // 得到反码
然后将反码转为原码(除二进制首位外,其余位数取反)
1000 0000 0000 0000 0000 0000 0000 1010 // 得到原码,转换为十进制为 -10
5 对应二进制补码 --> 0000 0000 0000 0000 0000 0000 0000 0101
先将补码取反
~0000 0000 0000 0000 0000 0000 0000 0101
------------------------------------------------------------------
1111 1111 1111 1111 1111 1111 1111 1010 // 得到取反后的补码
然后将补码转为反码,由于二进制首位是1,表示为负数,则需要先转为反码(在补码的基础上减1)
1111 1111 1111 1111 1111 1111 1111 1001 // 得到反码
然后将反码转为原码(除二进制首位外,其余位数取反)
1000 0000 0000 0000 0000 0000 0000 0110 // 得到原码,转换为十进制为 -8
5.位运算左移右移
左移
把整数a的各二进制位全部向左移动n位,高位丢弃,低位补0,左移n位其实就是乘以2的n次方
- 由于左移是丢弃最高位,0补最低位,所以符号位也会被丢弃,左移出来的结果值可能会改变正负性
正数左移
2 << 1; // 4
2的二进制表现形式:0000 0000 0000 0000 0000 0000 0000 0010
整体向左移动: 0000 0000 0000 0000 0000 0000 0000 0100 --> 4
负数左移
-2 << 1; // 4
-2的二进制表现形式:1000 0000 0000 0000 0000 0000 0000 0010
整体向左移动: 0000 0000 0000 0000 0000 0000 0000 0100 --> 4
注意:负数二进制首位的标志符被顶出去丢弃掉了
右移
把正数a的各二进制位全部向右移n位,保持符号位不变。右移n位其实就是除以2的n次方
- 为正数时,符号位为0,最高位补0
- 为负数时,符号位为1,最高位补1,使用补码进行移动
正数右移
2 >> 1; // 1
2的二进制表现形式:0000 0000 0000 0000 0000 0000 0000 0010
整体向左移动: 0000 0000 0000 0000 0000 0000 0000 0001 --> 1
负数右移
-2 >> 1; //
-2的二进制表现形式:1000 0000 0000 0000 0000 0000 0000 0010
先转换成反码: 1111 1111 1111 1111 1111 1111 1111 1101
再转换成补码: 1111 1111 1111 1111 1111 1111 1111 1110 // 反码的基础上+1
补码向右移动: 1111 1111 1111 1111 1111 1111 1111 1111 // 首位不动,向右移动,缺位补1
补码转成反码: 1111 1111 1111 1111 1111 1111 1111 1110 // 补码-1
反码转成原码: 1000 0000 0000 0000 0000 0000 0000 0001 // 转成十进制为 -1
左移右移应用场景
- 某个数乘以2的n次幂,使用左移n
- 某个数除以2的n次幂,使用右移n
6.练习
6.1 位运算判断奇偶数
#include <stdio.h>
int main(int argc, const char * argv[]) {
/*
分析:
1的二进制 --> 0000 0000 0000 0000 0000 0000 0000 0001
2的二进制 --> 0000 0000 0000 0000 0000 0000 0000 0010
3的二进制 --> 0000 0000 0000 0000 0000 0000 0000 0011
4的二进制 --> 0000 0000 0000 0000 0000 0000 0000 0100
5的二进制 --> 0000 0000 0000 0000 0000 0000 0000 0101
6的二进制 --> 0000 0000 0000 0000 0000 0000 0000 0110
7的二进制 --> 0000 0000 0000 0000 0000 0000 0000 0111
8的二进制 --> 0000 0000 0000 0000 0000 0000 0000 1000
总结出规律:奇数二进制最后一位为1,偶数二进制最后一位为0
*/
int n = 10;
if (n & 1) {
printf("奇数\n");
} else {
printf("偶数\n");
}
return 0;
}
6.2 位运算进行2个变量数据交换
#include <stdio.h>
int main(int argc, const char * argv[]) {
int a = 456456;
int b = 1045646;
printf("换位前: a --> %i b --> %i\n", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("换位后: a --> %i b --> %i\n", a, b);
return 0;
}