快速导航
【前言】
1.移位操作符
1.1左移操作符(<<)
1.2右移操作符(>>)
2.位操作符
2.1 & 按位与
2.2 | (按位或)
2.3 ^ (按位异或)
3.面试题目
3.1 交换两个变量(不创建临时变量)
3.2统计二进制中1的个数
3.2.1 方法一:右移(>>)和按位与(&)
3.2.2 方法二:使用取余操作符(%)
3.2.3 方法三:借助num&(num-1)
【前言】
相信有很多人对移位操作符和位操作符并不是很理解,没有关系,认真读完本篇文章,你对C操作符的理解会上升到一个新高度。
虽然说本篇文章是初阶篇,但是文章内容并不简单,操作的时候涉及到二进制位,那我们就先来看一看何为二进制位?
二进制位简称“位”,是二进制记数系统中表示小于2的整数的符号,一般用1或 0表示,是具有相等概率的两种状态中的一种。
二进制位的位数可表示一个机器字的字长,一个二进制位包含的信息量称为 - 比特。
1.移位操作符
移位操作符有两种:一种是左移操作符(<<),另一种是右移操作符(>>)。
这两种操作符都是对整数在存储在电脑中的二进制位进行操作(注意:移位操作符的操作数必须是整数)。
1.1左移操作符(<<)
接下来我们借助一个整数来更好的理解和应用左移操作符:
在计算机内存中存储的是二进制形式的补码,而正数的原码,反码,补码相同,5在内存中的补码:
移位操作符,移动的是二进制位,因为整数在内存中存储的是补码,所以移动的是内存中存储的补码 。
补码我们已经给出,接下来看一看移位操作符到底是怎么进行移位呢?
左移操作符移位原则:左边抛弃,右边补0。
#include <stdio.h>
int main()
{
int a = 5;
printf("%d\n", a << 1);
return 0;
}
5左移之后结果为10,看似是有乘2的效果的,那么是不是这样的呢?
下面我们来多举几个栗子验证一下。
#include <stdio.h>
int main()
{
int a = -1;
printf("%d\n", a << 1);
a = -10;
printf("%d\n", a << 1);
a = 100;
printf("%d\n", a << 1);
return 0;
}
负数左移的效果也是一样的(以-1为例):
结论:左移操作符确实有乘2的效果。
练习使用一下左移操作符:
原题链接:2的n次方计算_牛客题霸_牛客网
题目描述:不使用累加法的基础上,使用左移操作符(<<) 完成2的n次方的计算。
代码实现
#include <stdio.h>
int main()
{
int n = 0;
scanf("%d", &n);
printf("%d\n", 1 << n);
return 0;
}
1.2右移操作符(>>)
右移操作符和左移操作符是相似的,右移操作符和左移操作符最大的区别是在右移之后:
1.左边补符号位(算术右移)
2.左边补0(逻辑右移)
究竟是哪一种取决于程序的运行环境,不同编译器下右移方式可以不同。
int main()
{
int a = -1;
printf("%d\n", a >> 1);
return 0;
}
上面是VS2019下的运行结果,VS2019支持的是算术右移。
注意:移位操作符,不要移动负数位,这是标准未定义的;
对比一下左移操作符乘2效果,右移操作符是否具有除2的效果呢?
对于正数来说,可以认为具有除2的效果,但是不要忘了负数;我们在上面举例的-1右移之后还是-1,就不满足了。
所以不能直接说右移操作符具有除2的效果。
2.位操作符
位操作符,是对一个数的二进制位进行操作,两个操作数,且操作数必须是整数。
具体分为三种:& (按位与) |(按位或) ^(按位异或)。计算机中位运算操作,均是以二进制补码形式进行的
2.1 & 按位与
两个数的二进制相同位同为1时结果为1,否则为0。
下面看一下使用按位与(&)操作的两种情况 :
2.2 | (按位或)
两个数的相同二进制位同为0时结果为0,否则为1。
2.3 ^ (按位异或)
两个数相同二进制位相异时为1,相同是为0。
3.面试题目
3.1 交换两个变量(不创建临时变量)
在交换两个变量时,我们经常会借助第三个变量tmp;
比如交换a和b的值:
int tmp = a;
a = b;
b = tmp;//那么不创建临时变量的话我们怎么来交换两个变量呢?
使用异或操作符(^),异或的两个特征:0^num = num;num^ num = 0;
代码实现
int main()
{
int a = 10;
int b = 20;
printf("a=%d, b=%d\n", a, b);
a = a ^ b;
b = a ^ b;//b修改为a
a = a ^ b;//a修改为b
printf("a=%d, b=%d\n", a, b);
return 0;
}
缺陷:因为按位异或操作符的操作数只能是整数,所以交换的两个变量也必须是整数,只能完成两个整数的交换,无法完成两个浮点数的交换。
3.2统计二进制中1的个数
3.2.1 方法一:右移(>>)和按位与(&)
代码实现:
int getBinaryCount(int num)
{
int count = 0;//记录二进制中1的个数
int i = 0;
for (i = 0; i < 32; i++)
{
if (((num >> i) & 1) == 1)
{
count++;
}
}
return count;
}
3.2.2 方法二:使用取余操作符(%)
一个十进制的数,想要获取它的每一位,需要进行%10、/10;二进制的数也是一样的,可以通过%2、/2来获取每一位。
代码实现
int getBinaryCount(unsigned int num)
{
int count = 0;
while (num)
{
if(num % 2 == 1)
count++;
num /= 2;
}
return count;
}
这里解释一下为什么要传入unsigned int类型:使用负数举例,如果num是-1的话,进入循环一次之后除2就变为0,无法得到二进制中1的正确个数;
使用unsigned int的话会把-1的补码当成一个正数的补码,这是一个很大的数,可以进行以上操作。
3.2.3 方法三:借助num&(num-1)
代码实现
int getBinaryCount(int num)
{
int count = 0;
while (num)
{
num &= num - 1;
count++;
}
return count;
}