一篇文章带你学会移位和位操作符解析【画图详解】


前言

相较于其他操作符而言,移位操作符和位操作符不太常见,它们的运算方式比较复杂,而我也时常忘记它们的计算方法,现写一篇相关笔记,方便查看,也供各位读者查阅。


一、移位操作符

左移 <<

左移运算符<<
首先让我们来了解一下一些相关的基本定义。

移位操作符,移动的是二进制位,是存储在计算机中的二进制,相对于整数而言,存放在内存中的是数的补码,补码又是什么呢?让我们一起来了解下。
对于整数的二进制有3中表示形式:原码、反码、补码
正整数 -------------原码、反码、补码相同

负整数
原码 - 直接按照数字的正负写出的二进制序列
反码- 原码的符号位不变,其他位按位取反得到的
补码- 反码+1

说明一下,取反运算:二进制位按位取反
整数在内存中存储的是二进制的补码
举个例子
在这里插入图片描述
在这里插入图片描述

了解完原,反,补之后再来了解移位。
对于左移位而言,及二进制整体向左移动一位。
运算法则左边丢弃,右边补0
在这里插入图片描述
此时得到的数=1*23 + 1*21=10
对于负数而言,比正数复杂一些。
在这里插入图片描述
在这里插入图片描述

#include<stdio.h>

int main()
{
	int a = 5;
	//原反补:00000000000000000000000000000101
	int b = -1;
	//10000000000000000000000000000001 - 原码
	//11111111111111111111111111111110 - 反码
	//11111111111111111111111111111111 - 补码

	int c = a << 1;
	printf("%d\n", c);

	c = b << 1;
	printf("%d\n", c);
	return 0;
}

右移 >>

右移要比左移复杂一点,需要考虑符号位。
目前有两种右移
逻辑右移 - 右边丢弃,左边补0
算术右移 - 右边丢弃,左边补原符号位 (VS2019采用算术右移)

对于整数而言:
在这里插入图片描述
对于负数而言
在这里插入图片描述
在这里插入图片描述
可以看出vs2019是算术右移。

#include<stdio.h>

int main()
{
	int a = 5;
	//原反补:00000000000000000000000000000101
	int b = -1;
	//10000000000000000000000000000001 - 原码
	//11111111111111111111111111111110 - 反码
	//11111111111111111111111111111111 - 补码


	int c = a >> 1;
	printf("%d\n", c);

	c = b >> 1;
	printf("%d\n", c);

	return 0;
}

总结要点

需要注意的几点:

  • 移位运算符只针对整数
  • 对正整数而言,左移或右移可以理解为对数乘二之后的值或对数除二的值,负数则不一定,比如-1,右移还是-1。
  • 移位针对的是补码
  • 记住原反补的关系

二、位操作符

位操作符很厉害,但是不好用,但出现在代码中也难以理解,可读性较差。
位操作符都是针对二进制,具体是整数的补码。

按位与 &

按位与规则:相对应的位,只要有0就为0,两个都为1才得到1
细节方面请看代码:

#include<stdio.h>

int main()
{
	int a = 5;
	//原反补:00000000000000000000000000000101

	int b = -2;
	//10000000000000000000000000000010 - 原码
	//11111111111111111111111111111101 - 反码
	//11111111111111111111111111111110 - 补码


	int c = a & b;
	//按位与:相对应的位,只要有0就为0,两个都为1才得到1
	//a的补码:00000000000000000000000000000101
	//b的补码:11111111111111111111111111111110
	//按位与: 00000000000000000000000000000100  ->值为4
	printf("按位与:%d\n", c);
	return 0;
}

按位或 |

按位或规则:相对应的位,只要有1就为1,两个都为0才得到0。
需要注意负数的转码,切记负数的原反补,不同。下面代码有详细解释。

#include<stdio.h>

int main()
{
	int a = 5;
	//原反补:00000000000000000000000000000101

	int b = -2;
	//10000000000000000000000000000010 - 原码
	//11111111111111111111111111111101 - 反码
	//11111111111111111111111111111110 - 补码


	int c = a | b;
	//按位与:相对应的位,只要有1就为1,两个都为0才得到0
	//a的补码:00000000000000000000000000000101
	//b的补码:11111111111111111111111111111110
	//按位与: 11111111111111111111111111111111  

	//此时符号为1,即为负数,需要转码
	//c的补码:11111111111111111111111111111111
	//c的反码:11111111111111111111111111111110   //补码-1
	//c得原码:10000000000000000000000000000001   //符号位不变,其他位按位取反

	//此时值为 -1
	printf("按位或:%d\n", c);

按位异或 ^

按位或规则:相对应的位,相同为0,相异为1。

#include<stdio.h>

int main()
{
	int a = 5;
	//原反补:00000000000000000000000000000101

	int b = -2;
	//10000000000000000000000000000010 - 原码
	//11111111111111111111111111111101 - 反码
	//11111111111111111111111111111110 - 补码

	c = a ^ b;
	//按位与:相对应的位,相同为0,相异为1。
	//a的补码:00000000000000000000000000000101
	//b的补码:11111111111111111111111111111110
	//按位与: 11111111111111111111111111111011  

	//此时符号为1,即为负数,需要转码
	//c的补码:11111111111111111111111111111011
	//c的反码:11111111111111111111111111111010  //补码-1
	//c得原码:10000000000000000000000000000101  //符号位不变,其他位按位取反
	//此时值为 -5
	printf("按位异或:%d\n", c);

总结要点

  • 位操作符的操作数是整数。
  • 操作的是数的补码
  • 进行位运算时,符号位可能改变
  • 进行原反补相互转化时,符号位不变

变态面试题

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

没错,既然文章是关于位运算的,那解题思路自然也是这方面的。
实现两个数的交换,不能创建新的变量,那就只能用原来的变量存放一个和原来的两个值有关的值。
往位运算上面想。

没错,这个一般是真的想不到,下面给出不同的解法。

#include<stdio.h>

int main()
{
	int a = 3;
	int b = 5;

	int tmp = 0;
	printf("原始:a=%d b=%d\n", a, b);

	//1
	//常规方法,但是需要使用一个临时变量,不符合题意
	tmp = a;
	a = b;
	b = tmp;
	printf("1.a=%d b=%d\n", a, b);

	//2
	//溢出的问题,即a+b的值超过int类型的范围值
	a = 3;
	b = 5;
	a = a + b;
	b = a - b;
	a = a - b;
	printf("2.a=%d b=%d\n", a, b);


	//3
	//异或
	a = 3;
	b = 5;
	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	//缺点:
	//代码的可读性不够好
	//只适用于整型
	
	printf("3.a=%d b=%d\n", a, b);
	return 0;
}

在这里插入图片描述
现在我们来解释一下第三种方法,虽然想不到,但弄懂这个,下次再遇到的话也不会太慌。
在这里插入图片描述

三、运算符优先级表

在这里插入图片描述


总结

移位和位操作符,有很多易错点,也不容易一下就看懂,是比较难的。如有错误,欢迎大佬评论指正。

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

s_persist

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

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

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

打赏作者

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

抵扣说明:

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

余额充值