与进制有关的操作符

1.原码,反码,补码

  • 按照一个数的正负,直接写出它的二进制表示形式就是原码
  • 正数的原码、反码、补码是相同的
  • 负数的原码、反码、补码要经过计算的
    • 反码:原码的符号位不变,其他位按位取反
    • 补码:反码+1
  • 内存中存储的形式是:补码的二进制,所以在参与移位时,移动后都是补码

int n = 10,整形占四个字节(32bit)
00000000000000000000000000001010 原码
00000000000000000000000000001010 反码
00000000000000000000000000001010 补码
int n = -10
10000000000000000000000000001010 原码
111111111111111111111111111111110101 反码
111111111111111111111111111111110110 补码

原码、反码、补码相互转换图:
在这里插入图片描述
这里可以看出:原码到补码与补码到原码的操作实际上是一样的


2.移位操作符

左移操作符 <<

左移操作符:将补码向左移动,在右补0
要注意:b = a<<1左移运算符不是直接将a左移,而是将左移后的值保存到b中,a的值是不变的
如果想让a的·值变:a = a<<1a<<=1

int main()
{
	//对正数的左移
	int a = 10;
	
	int b = a << 1;
	printf("%d\n", a);
	printf("%d\n", b);
}

00000000000000000000000000001010 -10的补码,进行左移
在这里插入图片描述
左移后的补码:00000000000000000000000000010100
输出结果:
在这里插入图片描述

int main()
{
	//对负数的左移
	int a = -10;
	int b = a << 1;
	printf("%d\n", a);
	printf("%d\n", b);
}

a的补码:111111111111111111111111111111110110
111111111111111111111111111111101100 -b的补码
10000000000000000000000000010100 -b的原码
输出结果:
在这里插入图片描述

从10与-10的左移一位的结果中,可以看出左移一位有扩大2倍的作用


右移操作符 >>

右移操作符分为两种
1.算术右移(常见):右边丢弃,左边补原来的符号位
2.逻辑右移:右边丢弃,左边直接补0
右移的方式取决于编译器,但是常见的右移是算术右移


对于移位运算符,不要移动负数位,这个是标准未定义的。
例如:

int num = 10;
num>>-1;//error

3.位操作符

位操作符有:

& -按位与
| -按位或
^ -按位异或

它们的操作数必须是正数,并且针对二进制计算


按位与&

对应的二进制有0就为0,只有对应的两个都为1时,才为1

int main()
{
	int a = 3;
	//00000000000000000000000000000011 a的补码
	int b = -5;
	//10000000000000000000000000000101
	//11111111111111111111111111111010
	//11111111111111111111111111111011  b的补码
	int c = a&b;
	//00000000000000000000000000000011 a的补码
	//11111111111111111111111111111011  b的补码
	//00000000000000000000000000000011  c的补码
}

按位或 |

对应的二进制位,有1则位1,两个同时为0则为0

int main()
{
	int a = 3;
	//00000000000000000000000000000011 a的补码
	int b = -5;
	//10000000000000000000000000000101
	//11111111111111111111111111111010
	//11111111111111111111111111111011  b的补码
	int c = a|b;
	//00000000000000000000000000000011 a的补码
	//11111111111111111111111111111011  b的补码
	//11111111111111111111111111111011  c的补码
}

按位异或 ^

对应的二进制位:相同位0,相异为1

int main()
{
	int a = 3;
	//00000000000000000000000000000011 a的补码
	int b = -5;
	//10000000000000000000000000000101
	//11111111111111111111111111111010
	//11111111111111111111111111111011  b的补码
	int c = a^b;
	//00000000000000000000000000000011 a的补码
	//11111111111111111111111111111011  b的补码
	//11111111111111111111111111111000  c的补码
}

异或操作赋一些特点:

1.a^a = 0
2.0^a = a
3.异或支持交换律

与^有关的变态面试题

不能创建临时变量(第三个变量),实现两个数的交换

#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^b,接着b = a^b,其实就是b = a^b =a ^ b ^b = a,a = a^ b = a^b ^a,也就完成了交换

4.几道例题

这里,我们先想一下怎么得到二进制数字中的最后一个数字?

我们想一下求十进制数字的每一位:以11为例,11%2 = 1得到个位数字,11/2得到取出个位后的数字
其实二进制求每一位上数字一样
在这里插入图片描述

使用取模运算,除法运算是一种方法,同时也可以使用操作符进行计算
实际上,用1与其他数字做按位与&就可以得到最后以为二进制数字,因为1的二进制最后一位为1,其余为0,&的规则是:有0就为0,都是1才为1,所以得到的数字前面各位都为0,而最后一位由原数字决定,它最后一位为0,结果就为0.为1,结果就为1
在这里插入图片描述
接下来,为了得到去掉最后一位的剩余数字,只需把数字向右移动1即可 11>>1


前面的基础比较容易理解,但是在做题时有的方法我们仍难以想到,所以我们接下来看几道题理解理解

1.求一个数的二进制形式中有多少个1

  • 使用前面的取模除法运算:
//二进制中1的个数
int NumberOf1(int n)
{
	int count = 0;
	while (n)
	{
		
		if (n % 2 == 1)   //如果最后一位为1.count+1
		{
			count++;
		}
		n = n / 2;   
	}
	return count;
}

这里的循环条件位n>o因为只要不是数字0,其余数字的二进制数字中都会有1,所以n>0时进行循环,如果退出循环来,就说明剩下的数字中一定没有1

  • 使用&,>>
int NumberOf1(int n)
{
	int count = 0;
	while (n>0)
	{
		int a = n & 1;
		if (a==1)
		{
			count++;
		}
		n = n >> 1;
	return count;
}

前面两种算法计算正数都可以,但是负数就计算不了,负数连while循环都进不去,所以我们不能使用n>0作为循环条件
不论正数还是负数,都是4个字节大小,32个比特位,所以我们需将二进制循环遍历32次求出每位上的数字即可

int NumberOf1(int n)
{
	int i = 0;
	int count = 0;
	for (i = 0; i < 32; i++)  //循环32次,求出每位上的数字
	{
		if (((n >> i) & 1) == 1)
		{
			count++;
		}
		
	}
	return count;
}

((n >> i) & 1),将n右移i次,i从0开始,每次加一,所以n从右移1位开始,每次多移一位,因为左右移操作符不会改变原来n的值,所以每次向后移动i位,就巧妙地得到了i位上的数字,在&1就能得到每位上的数字


这里有一个几乎想不到的求法,用n = n&(n-1)
在这里插入图片描述
所以计算n = n&(n-1)使用的次数,就可以得出1的个数

int NumberOd1(int n)
{
	int count = 0;
	while(n)
	{
		n = n&(n-1);
		count++;
	}
	return count++;
}

2.获取一个整数二进制序列中所有的偶数位和奇数位,分别打印出二进制序列

void print(int n)
{
	int arr[32] = { 0 };
	printf("奇数位:");
	for (int i = 1; i < 32; i+=2)
	{
		printf("%d ", (n>>i)&1);
		arr[i] = (n >> i) & 1;

	}
	printf("\n");
	printf("偶数位:");
	for (int i = 0; i < 32; i += 2)
	{
		printf("%d ", (n >> i) & 1);
		arr[i] = (n >> i) & 1;
	}
	printf("\n");


	for (int i = 31; i >=0; i--)
	{
		printf("%d", arr[i]);
	}
}

这里的原理其实和上一题一样,只是分为奇数和偶数,这里仍是对32位都遍历,只是每次i增加2
i从1开始,每次增加2,这就是在遍历奇数位
i从0开始,每次增加2,这就是在遍历偶数位


3.两个int(32位)整数m和n的二进制表达中,有多少个位(bit)不同?

int compare(int a, int b)
{
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if (((a >> i) & 1) != ((b >> i) & 1))
		{
			count++;
		}
	}
	return count;
}

这里也是,用(n>>i)&1得到两个数字的各个位,然后进行比较,看是否相等

其实这里也可以使用按位异或操作符^,如果对应每位相同位0,相异为1,在去求a^b中1的个数

int compare(int a, int b)
{
	//也可以用异或解决
	int tmp = a ^ b;
	//统计tmp里1的个数
	int count = 0;
	for (int i = 0; i < 32; i++)
	{
		if ((tmp >> i) & 1 == 1)
		{
			count++;
		}
	}
	return count;
}


最后,其实在二进制的一些运算中,十分有趣,不时会让人感觉妙啊·~~

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

疯癫了的狗

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值