首先应该明白一个规则,位运算符全部都是基于二进制的规则运算的,而且他们大部分的返回值都是0/1值的二进制转换为十进制后的结果,其计算原理为比较对应二进制相同位上的0/1值
位运算符有与(&),非(~),或(|),异或(^),左移(<<),右移(>>)
与(&)
对应位同为1,结果为1,其余为零.

就像这样,15对应的二进制为00001111,127对应的二进制位为0111111,比如:
第一位,二者都为0,故最终结果为0,第五位,二者都为1,结果为1.第二位,一个为1,一个为0,最终结果为0。
最终完整计算结果为00001111,它转换为十进制为15,所以
int a=15,b=127;
(a&b)=15;
或(|)
对应位只要有一个为1 结果位 就为1,其余情况为0;

int a=127,b=128;
(a&b)=255;
非(~)
一个二进制操作数,对应位为0,结果位为1;对应位为1,结果位为0;所以非是一个单运算符
比如: 十进制 1 的二进制表示为:0000 0001,每位都取反为:11111110
而如果再换成十进制的话,就是255
这时候就有必要补充一下源码,反码和补码的概念了
1.原码就是这个数本身的二进制形式。
例如:0000001 就是+1,1000001 就是-1
2.正数的反码和和原码相同。
负数的反码是将其原码除符号位之外的各位求反。
[-3]反=[10000011]反=11111100
3.正数的补码和源码相同。
负数的补码是将其原码除符号位之外的各位求反之后在末位再加1。
[-3]补=[10000011]补=11111101
换成十进制来理解,3的反码和补码都是3,而-3的反码为252,补码为253
这里可以发现,非的作用其实就是无论正负都取其反码

异或(^)
两个二进制操作数对应位相同为0,不同为1;

移位(<<,>>)
左移运算是将一个二进制位的操作数按指定移动的位数向左移动,移出位被丢弃,右边移出的空位一律补0。右移运算是将一个二进制位的操作数按指定移动的位数向右移动,移出位被丢弃,左边移出的空位一律补0,或者补符号位
左移

举个例子,十进制10左移两位就是40,对于数学意义来说,在数字没有溢出的前提下,对于正数和负数,左移一位都相当于乘以2的1次方,左移n位就相当于乘以2的n次方
右移
对于正数,类似于左移,补零就可,但是对于负数,右移的操作就与左移不同了

这里对于负数的操作需要特别注意,由于负数的最高位是符号位1,所以,符号位无需动,先求其补码,再右移两位,高位补1而不是零,再求其补码即为最终结果
同样也具有数学意义,右移一位相当于除2,右移n位相当于除以2的n次方。
那么既然如此,8>>2=2,那么如果不是整除,比如9>>2呢,下面做出研究,在Vscode中写以下代码
#include <stdio.h>
int main(){
int a,b,c;
a=9,b=-9,c=-7;
int d,e,f;
d=a>>2;
e=b>>2;
f=c>>2;
printf("%d %d %d",d,e,f);
}
输出结果:
2 1 -3 -2
由此可见,对于正数,会采取截断措施
而对于负数,会采取进1措施
或者干脆这么理解,都取较小的哪个数