引言
位操作符是用于在二进制位级别上操作数据的运算符。以下是C语言中的位操作符:
位操作符 | 名称 |
& | 按位与 |
| | 按位或 |
^ | 按位异或 |
~ | 按位取反 |
<< | 按位左移 |
>> | 按位右移 |
c语言中存在6个位操作运算符,且它们只能用于整形操作数。
原码、反码、补码
为了方便我们理解位操作符,我们需要了解原码、反码和补码的相关知识
原码、反码、补码是计算机中用于表示和处理有符号整数的三种编码方式。它们在数字的表示和运算中发挥着重要的作用。
原码
在原码中,整数由三部分组成:符号位、整数部分的二进制表示。
最高位是符号位,0表示正数,1表示负数。
例如,+5的原码是 00000000 00000000 00000000 00000101
-5的原码是10000000 00000000 00000000 00000101
反码
反码是在原码的基础上,对正数不变,负数的二进制表示取反(0变为1,1变为0)。
仍然使用符号位表示正负。
例如,+5的反码是00000000 00000000 00000000 00000101
-5的反码是11111111 11111111 11111111 11111010
补码
补码是在反码的基础上,将其加1。
补码的优势在于加法运算的一致性,减法可以看作是加法的补数操作,简化了电路设计。
仍然使用符号位表示正负。
例如,+5的补码是00000000 00000000 00000000 00000101
-5的补码是11111111 11111111 11111111 11111011
总结:正数的原码反码补码均相同
负数的原码反码补码均不同
通常在计算机中,整型变量(包括有符号整数)是以补码形式存储的。补码表示法在进行加法和减法运算时更为方便,而且只有一个零的表示,使得处理正负数的操作更加统一。
&(按位与)
运算规则
对于每一对操作数的对应位,如果两个操作数的对应位都是1,则结果中的对应位为1,否则为0。
0 & 0 = 0 |
0 & 1 = 0 |
1 & 1 = 1 |
示例
int a=3; //00000000 00000000 00000000 00000011
int b=5; //00000000 00000000 00000000 00000101
int c=a&b; //00000000 00000000 00000000 00000001
应用场景
位操作符可以用于屏蔽特定的二进制位,例如:
00000000 00000000 00000000 10001111 & 00000000 00000000 00000000 00000111
得到的结果为00000000 00000000 00000000 00000111
这样就可以屏蔽特定的二进制位了。
&可以利用0来屏蔽,也能用1来读取特定的二进制位,例如:
00000000 00000000 00000000 10111111 & 00000000 00000000 00000000 00001111
得到的结果为00000000 00000000 00001111
这样我们就可以得到后面四位。
总结:在二进制中,&1为不变,&0为屏蔽。
|(按位或)
运算规则
对于每一对操作数的对应位,如果两个操作数的对应位中至少有一个是1,则结果中的对应位为1,否则为0。
0 | 0 = 0 |
0 | 1 = 1 |
1 | 0 = 1 |
1 | 1 = 1 |
示例
int a=3; //00000000 00000000 00000000 00000011
int b=5; //00000000 00000000 00000000 00000101
int c=a|b; //00000000 00000000 00000000 00000111
应用场景:
按位或操作通常用于将特定位设置为1或保留特定位的值。
如:00000000 00000000 00000000 10110000
00000000 00000000 00000000 00001111
进行按位或操作之后就变成:
00000000 00000000 00000000 10111111
总结:在二进制中,|1为置1,|0为不变。
^(按位异或)
运算规则
对于每一对操作数的对应位,如果两个操作数的对应位中只有一个是1,则结果中的对应位为1,否则为0。即只要参与运算的双方互异,结果就为1,否则为0。
0 ^ 0 = 0 |
0 ^ 1 = 1 |
1 ^ 0 = 1 |
1 ^ 1 = 0 |
示例
int a=3; //00000000 00000000 00000000 00000011
int b=5; //00000000 00000000 00000000 00000101
int c=a^b; //00000000 00000000 00000000 00000110
应用场景
可以对特定位进行0 1反转。
如:00000000 00000000 00000000 11101100
00000000 00000000 00000000 00001100
进行按位异或操作后:
00000000 00000000 00000000 11100000
用于在不影响其他位的情况下交换两个值或执行其他与翻转位相关的操作。
当我们想要实现两个变量的数值交换,我们可以选择通过创建一个新的变量作为中间变量,进而实现数值交换这个功能,但在我们学习了按位异或操作符之后,就可以不创建新变量来交换数值。
int a=3;
int b=5;
a=a^b;
b=a^b; //b=a^b^b=a
a=a^b; //b=a^b^a=b
~(按位取反)
运算规则
对于操作数的每一位,按位取反操作将0变为1,1变为0。
按位取反的运算与原码、反码、补码的转换有关。
示例
int a=5; //00000000 00000000 00000000 00000101 ——补码
int b=~a; //11111111 11111111 11111111 11111010 ——取反
这个时候得到的是补码,我们需要将其转换为原码
取反后的补码:11111111 11111111 11111111 11111010
反码:11111111 11111111 11111111 11111001
补码:10000000 0000000 00000000 00000110
最终答案为-6
再来看一个:
int a=-1; // 原码:10000000 00000000 00000000 00000001
// 反码:11111111 11111111 11111111 11111110
// 补码:11111111 11111111 11111111 11111111
int b=~a; //取反后的补码:00000000 00000000 00000000 00000000
取反后的补码为00000000 00000000 00000000 00000000,所以最终结果为0.
应用场景
在处理标志位或状态时,可以使用按位取反操作来翻转特定位的值。
PS:~的优先级是位操作符中最高的,必须优先计算。
<<(按位左移)
语法
result = value << n
运算规则
其中, value 是要进行左移操作的数, n 是要左移的位数, result 是左移后得到的结果。
对运算符<<左边的运算量的每一位全部左移右边运算量表示的位数,右边空出的位补0。
示例
int value=1; //00000000 00000000 00000000 00000001
int n=2;
int result=value<<2; //00000000 00000000 00000000 00000100
应用场景
乘法运算的优化:乘法运算的优化,特别是乘以2的幂次方的情况。
int main()
{
int value=2; //00000000 00000000 00000000 00000010
int n=3;
int result=value<<n; //00000000 00000000 00000000 00010000
printf("%d",result);
}
左移n位相当于乘2^n
位操作: 可以用于修改特定位的值
int main()
{
int flags = 0;
flags |= (1 << 2); // 设置第3位为1
// 检查第3位是否为1
if (flags & (1 << 2))
{
printf("第3位为1\n");
}
else
{
printf("第3位为0\n");
}
return 0;
}
>>(按位右移)
语法
result = value >> n
运算规则
右移运算符分为两种:
1.逻辑右移:左边用0填充,右边丢弃
2.算数右移:左边用原该值的符号位填充,右边丢弃
示例
逻辑右移
int a=-1; //补码:11111111 11111111 11111111 11111111
int n=1;
int b=a>>n; //a>>n:01111111 11111111 11111111 11111111
//用0填充
算数右移
int a=-1; //补码:11111111 11111111 11111111 11111111
int n=1;
int b=a>>n; //a>>n:11111111 11111111 11111111 11111111
//用符号位1填充
应用场景
除法运算的优化:
int main()
{
int value=16; //00000000 00000000 00000000 00010000
int n=3;
int result=value>>n; //00000000 00000000 00000000 00000010
printf("%d",result);
}
右移n位相当于除以2^n
提取二进制数的部分位:
如:当我们想提取 00000000 00000000 00000000 11011000 中的1101
我们可以通过按位与和右移操作,可以从一个整数中提取出指定位置的部分位。
原始状态:00000000 00000000 00000000 11011000
右移后:00000000 00000000 00000000 00001101
& 00000000 00000000 00000000 00001111
最终结果:00000000 00000000 00000000 00001101
结束语
ok了家人们,我终于将这些基础知识整理了一遍。希望各位大佬们能给予一些支持与鼓励~~
在文章中有什么错误也欢迎大佬们指出!!!
十分感谢!!!