2.2 左移操作符
移位规则:左边抛弃,右边补0
我们来看一段代码:
#include <stdio.h> int main() { int a = 3; //3的二进制序列:00000000 00000000 00000000 00000011 int b = a << 1; printf("%d\n", b); printf("%d\n", a); return 0; }
效果展示:
Q:为什么 a << 1 变成了6呢?
A:我们使用图解:
3是正整数,原码就是补码。
这里应该注意的是 a ,对 a 进行了左移并将值赋给 b ,a 本身是不发生变化的。a << 1 这个表达式结果是 6 ,但是 a 是不变的。并且舍弃的确实是符号位,舍弃后紧挨着的那个数字充当符号位。
这次我们来看看 -3
#include <stdio.h> int main() { int a = -3; //原码二进制序列:10000000 00000000 00000000 00000011 //反码二进制序列:11111111 11111111 11111111 11111100 //补码二进制序列:11111111 11111111 11111111 11111101 int b = a << 1; //补码二进制序列:11111111 11111111 11111111 11111010 //反码二进制序列:10000000 00000000 00000000 00000101 //原码二进制序列:10000000 00000000 00000000 00000110 printf("%d\n", b);//打印是以原码来打印 printf("%d\n", a); return 0; }
效果展示:
我们继续画图分析:
注:负数的补码转换成反码有两种方式:-1,取反;取反+1。两种方式都是可以的。
2.3 右移操作符
移位规则:
右移运算分两种:
1.逻辑移位
左边用 0 补充,右边丢弃
2.算术移位
左边用原来值的符号位填充,右边丢弃
注:两种都是移几位相应补几位。
我们继续以代码来深入了解:
#include <stdio.h> int main() { int a = -5; int b = a >> 1; printf("%d\n", b); printf("%d\n", a); return 0; }
效果展示:
Q:为什么 b 变成了-3 呢?
我们来分析一下,有了左移的经验,这里就会容易不少。
这里我们也可以了解到 vs 2019 编译器做的是算数右移。
如果这里是逻辑右移的话符号位就是 0 ,得出来的就是 3。
警告:对于移位运算符,不要移动负位数,这个是标准未定义的。
3、位操作符
位操作符有:
& //按位与
| //按位或
^ //按位异或
注:它们的操作数必须是整数。
这里的位:是二进制位。
3.1 按位与
我们来看一段代码:
#include <stdio.h> int main() { int a = 3; int b = -5; int c = a & b;//按位与:对应的二进制位上有0为0,双1为1 //00000000 00000000 00000000 00000011 -- 3的补码 //10000000 00000000 00000000 00000101 -- -5的原码 //11111111 11111111 11111111 11111010 -- -5的反码 //11111111 11111111 11111111 11111011 -- -5的补码 //00000000 00000000 00000000 00000011 -- 3的补码 //00000000 00000000 00000000 00000011 -- &的结果,这里的补码和3的补码是一样的,因此打印的就是3 printf("%d\n", c); return 0; }
按位与的规则是:对应的二进制位上有0为0,同是1为1。
按照这个规则我们来分析一下 c 的补码是:
11111111 11111111 11111111 11111011 -- -5的补码
00000000 00000000 00000000 00000011 -- 3的补码
00000000 00000000 00000000 00000011 -- &的结果,符号位是 0 (表示正数),原反补码是相同的。
这里的补码和3的补码是一样的,因此打印的就是3。
效果展示:
3.2 按位或
按位或的规则是:对应的二进制位上有1为1,同是0为0
我们来看一段代码:
#include <stdio.h> int main() { int a = 3; int b = -5; int c = a | b;//按位或:对应的二进制位上有1为1,双0为0 //00000000 00000000 00000000 00000011 -- 3的补码 //10000000 00000000 00000000 00000101 -- -5的原码 //11111111 11111111 11111111 11111010 -- -5的反码 //11111111 11111111 11111111 11111011 -- -5的补码 //00000000 00000000 00000000 00000011 -- 3的补码 //11111111 11111111 11111111 11111011 -- |的结果 printf("%d\n", c); return 0; }
按照这个规则我们来分析一下 c 的补码是:
11111111 11111111 11111111 11111011 -- -5的补码
00000000 00000000 00000000 00000011 -- 3的补码
11111111 11111111 11111111 11111011 -- |的结果(这里的补码和 -5 的补码是一样的)
这里的符号位为 1 (代表是负数),因此这需要将补码转为原码。
原码:10000000 00000000 00000000 00000101
c 的原码转为 10 进制就是 -5。
效果展示: