一、二进制位
想要弄明白位运算操作符、逻辑操作符,首先要知道整型在计算机内存中的存储方式
注:移位操作符、位操作符针对的必须是整型数据
我们较为熟悉的是十进制,可以用十进制类比二进制
十进制中数字的范围是0~9,不会出现10,同理,二进制只有数字0和1,没有2及以上的数字
下面用111这个数字来举例
如果这是一个十进制数字,那么它的大小是1 * 10 ^ 2 + 1 * 10 ^ 1 + 1 * 10 ^ 0 = 111
如果这是一个二进制数字,那么它的大小是1 * 2 ^ 2 + 1 * 2 ^ 1 + 1 * 2 ^ 0 = 7
也就是说,一个n进制的数字,如果第m位上的数字是p,那么这一位表示的大小是p * n ^ (m - 1)
下面介绍整型数据的原码、反码、补码,由原码得到补码的基本运算规则就是:
原码符号位不变,其他位按位取反得反码;反码+1得补码
反过来,由补码得到原码:
补码-1得反码;反码符号位不变,其他位按位取反得原码
对于一个数字,我们可以很轻松的写出它的原码,经过上面的运算得到它的补码后,便可进行位运算操作和逻辑操作
注1:移位操作和位操作针对的都是数据的补码
注2:正整数在内存中的原码、反码和补码相同,存储时最高位(即符号位)是0
注3:负整数的补码则需通过上面的运算规则计算得出,存储时最高位(即符号位)是1
二、移位操作符
1.左移操作符 <<
左移操作符:左边丢弃,右边补0(相当于对这个数字乘2)
代码如下(示例):
int main()
{
int a = 5;
//00000000 00000000 00000000 00000101 5的原码、反码、补码
//内存中存的是补码
int b = a << 1;
//00000000 00000000 00000000 00001010 b的原码、反码、补码
printf("b = %d\n", b);//b = 10
}
由于a是正数,所以a的原码、反码和补码都相同,为
00000000 00000000 00000000 00000101
左移操作符规则是左边丢弃,右边补0,所以运算后b的原码、反码和补码为
00000000 00000000 00000000 00001010
转化为10进制就是10
2.右移操作符 >>
右移操作符的运算规则与左移操作符类似:右边丢弃,左边补0或1
但是由于左边最高位涉及到符号的问题(0表示正数,1表示负数),所以右移操作分为逻辑右移和算术右移
(1)算术右移
运算规则:左边补原来的符号位,右边丢弃。
代码如下,具体的运算细节已用注释写出:
int main()
{
int a = 5;
int b = a >> 1;
//00000000 00000000 00000000 00000101 5的原码、反码、补码
//00000000 00000000 00000000 00000010 b的原码、反码、补码
printf("b = %d\n", b);//b = 2
a = -5;
int c = a >> 1;
//10000000 00000000 00000000 00000101 -5的原码
//11111111 11111111 11111111 11111010 -5的反码
//11111111 11111111 11111111 11111011 -5的补码
//11111111 11111111 11111111 11111101 c的补码
//11111111 11111111 11111111 11111100 c的反码
//10000000 00000000 00000000 00000011 c的原码
printf("c = %d\n", c);//c = -3
//说明当前编译器(VS2013)默认是算术右移
return 0;
}
(2)逻辑右移
与算数右移同理,只需在移位后最高位补0即可,此处不再赘述。
二、位操作符
首先要明确位操作符中的“位”是指二进制数的补码的比特位
1.按位与&
按位与的运算规则:二进制位中有0就是0,全1才为1
代码如下,具体的运算细节已用注释写出:
int main()
{
int a = -1;
int b = -2;
int c = a & b;
//10000000 00000000 00000000 00000001 a原码
//10000000 00000000 00000000 00000010 b原码
//按位与
//10000000 00000000 00000000 00000000
//用原码计算是错误的
//c的结果应该是-2,很显然逻辑运算用的是补码而不是原码
printf("%d\n", c); //-2
//11111111 11111111 11111111 11111111 a补码
//11111111 11111111 11111111 11111110 b补码
//按位与
//11111111 11111111 11111111 11111110 -2
}
2.按位或 |
按位或的运算规则:二进制位中有1就是1,全0才为0
代码如下,具体的运算细节已用注释写出(方便起见,用正数运算):
int main()
{
int a = 5;
int b = 3;
//00000000 00000000 00000000 00000101 a
//00000000 00000000 00000000 00000011 b
//按位或
//00000000 00000000 00000000 00000111 c
int c = a | b;
printf("%d\n", c);//c = 7
}
3.按位异或 ^
按位异或的运算规则:二进制位相同为0,相异为1
代码如下,具体的运算细节已用注释写出:
int main()
{
int a = 5;
int b = 3;
//00000000 00000000 00000000 00000101 a
//00000000 00000000 00000000 00000011 b
//00000000 00000000 00000000 00000110 c
int c = a ^ b;
printf("%d\n", c);//c = 6
}