目录
例题2.编写代码实现:求一个整数存储在内存中的二进制1的个数
一、移位操作符
1)左移操作符—— <<
移位规则:左边抛弃、右边补0
以一段代码来讲解:
#include <stdio.h>
int main()
{
int num = 10;
int n = num << 1;
printf("n=%d\n", n);
printf("num=%d\n", num);
return 0;
}
首先,我们知道正数的原码、反码、补码是相同的,所以:
10的原码为:00000000 00000000 00000000 00001010
10的反码为:00000000 00000000 00000000 00001010
10的补码为:00000000 00000000 00000000 00001010
对于左移操作符(即<<),顾名思义就是向左移动x位,在上述代码中对应的x为1,根据移位规则,将补码向左移动移位,然后在后边补0,即下图所示。
由于符号位是0,整数的原码和补码是相同的,即10左移1位后的数为20。
2)右移操作符—— >>
右移运算有两种,一是逻辑右移,二是算数右移。
①逻辑右移:左边用0填充,右边丢弃
②算数右移:左边用原该值的符号位填充,右边丢弃
在不同的编译器中可能不同,不过大部分编译器都是算数右移,在VS上亦是如此。
方法与上述类似,我们再以上述的代码为例,10算数右移一位后是多少呢?答案是5,方法与上述类似,不过多赘述了。
注意:移位操作符的操作数只能是整数。
二、位操作符
位操作符与移位操作符一样都是对补码进行操作的,并且他们的操作数必须是整数。我们先看一段代码然后进行讲解。
#include <stdio.h>
int main()
{
int m = -3;
int n = 5;
printf("%d\n", m & n);
printf("%d\n", m | n);
printf("%d\n", m ^ n);
printf("%d\n", ~0);
return 0;
}
1)按位与—— &
简单来说,就是两个都为1才为1,有一个为0则为0。
先来回顾一下负数的原码、反码、补码,负数的反码是在原码的基础上,除符号位外其余取反,而补码则是在反码的基础上再加1。
-3的原码为:10000000 00000000 00000000 00000011
-3的反码为:11111111 11111111 11111111 11111100
-3的补码为:11111111 11111111 11111111 11111101
5的补码为:00000000 00000000 00000000 00000101
将-3和5的补码对照来看,每个位对齐,都为1则为1,有0则为0,得到:00000000 00000000 00000000 00000101,由于为正数,其补码与原码相同,即(-3)& 5为5。
2)按位或—— |
简单来说,就是有一个为1则为1,两个都为0则是0。
-3的补码为:11111111 11111111 11111111 11111101
5的补码为:00000000 00000000 00000000 00000101
按照此规则,得到11111111 11111111 11111111 11111101,这是补码,补码取反加1可以得到原码,即10000000 00000000 00000000 00000011,即-3。
3)按位异或—— ^
简单来说,就是相同的则为0,不同的则为1。
-3的补码为:11111111 11111111 11111111 11111101
5的补码为:00000000 00000000 00000000 00000101
我们很容易得到按位异或后的补码为:11111111 11111111 11111111 11111000,其原码为10000000 00000000 00000000 00001000,即-8。
4)按位取反—— ~
每一位都依次取反,1变为0,0变为1,包括符号位。
0的补码为 00000000 00000000 00000000 00000000,按位取反后:11111111 11111111 11111111 11111111,在这个补码的基础上再取反加1得到原码:10000000 00000000 00000000 00000001,即-1。
三、例题
例题1.不创建临时变量,实现两个整数的交换
方法一:
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a + b;
b = a - b;
a = a - b;
printf("a=%d,b=%d\n", a, b);
return 0;
}
方法二:用^解决。
首先我们需要知道对于一个整数a,a ^ a = 0, 0 ^ a = a,这其实比较好理解,根据规则,相同为0,不同为1,a与a是完全相同的,所以,每一位都是0,其原码也为0,而0补码每一位都是0,对于a补码中的每一个1都是不同的,所以对于位为1,所以最后得到的补码与a的补码完全一样,所以0 ^ a = a。
#include <stdio.h>
int main()
{
int a = 10;
int b = 20;
a = a ^ b;
b = a ^ b; //相当于b = a ^ b ^ b,而b ^ b = 0, 0 ^ a = a
a = a ^ b; //相当于 a = a ^ b ^ a ,而 a ^ a = 0, 0 ^ b = b
printf("a=%d,b=%d\n", a, b);
return 0;
}
例题2.编写代码实现:求一个整数存储在内存中的二进制1的个数
方法一:
我们算十进制的123中有多少1时可以想到用123 % 10,再用123 / 10的方法,一直循环到为0,算二进制中1的个数同样也可以如此。
#include <stdio.h>
int main()
{
int num = 10;
int count = 0;
while (num)
{
if (num % 2 == 1)
count++;
num = num / 2;
}
printf("二进制中1的个数为:%d\n", count);
return 0;
}
方法二:对于一个整数,有32位,每一位都与上1,如对应位为1则count++。
#include <stdio.h>
int main()
{
int num = 10;
int i = 0;
int count = 0;
for (i = 0; i < 32; i++)
{
if (num & (1 << i)) //1<<i 得到00000001、 00000010...
count++;
}
printf("二进制中1的个数为:%d\n", count);
return 0;
}
方法三:
#include <stdio.h>
int main()
{
int num = 10;
int i = 0;
int count = 0;
while (num)
{
count++;
num = num & (num - 1);
}
printf("二进制中1的个数为:%d\n", count);
return 0;
}
下图是循环的过程:
其实也很好理解,每次减1后,原来为1的那一位变为0,其后一位变为1(与十进制减法一样的道理),然后与原来数与,并改变该数(num = num&(num-1))就少了一个1,即每次循环时都少一个1,直至为0。
于此,就讲解结束啦!