「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」

文章目录

  • 移位操作符、位操作符、二进制(C语言)
    • 一、移位操作符
      • 1. 二进制知识补充
        • <1>二进制的书写:
        • <2>二进制码的三种表现形式:
      • 2. 移位操作符
        • <1> ">>"
        • <2> "<<"
    • 二、位操作符(操作数必须为整数)
      • 1. ' & ' 按位与
      • 2. ' | ' 按位或
      • 3. ' ^ ' 按位异或
    • 三、应用
      • 1. 不创建临时变量,实现两数交换
      • 2. 求一个整数二进制数中1的个数

移位操作符、位操作符、二进制(C语言)

大家好,今天给大家带来的是移位操作符、位操作符、二进制详解。
(本章花费3.5小时,若对你有帮助,还请点个免费的赞)
我是黎明_CL,那接下来我们进入主题!

一、移位操作符

  • “<<” 左移操作符
  • “>>” 右移操作符
    核心:移位操作流程为:将数写为原码—>反码—>补码—移位—补码—>反码—>原码——数
  • 计算机能识别的是二进制信息 0 1
    • 使用位移操作符对整型的二进制序列进行有效数位的左右移动;
      移动的是计算机的二进制位,所以接下来我们先讨论整数的二进制表现形式:
      首先先了解下二进制的相关知识

1. 二进制知识补充

<1>二进制的书写:

由于我们熟悉10进制,所以我们根据10进制来学习二进制

  • 10进制
    每位都是有权重的,例如对于22:
    「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」_第1张图片
<2>二进制码的三种表现形式:
  1. 原码
  2. 反码
  3. 补码——能被机器识别的
    不管是正负都可以写出其原码,根据正负直接写出的二进制序列就是原码
  • 正整数的原码、反码、补码是相同的
  • 负整数的原码、反码、补码是要计算的:
    • 反码符号位不变,其它位按位取反
    • 补码=反码+1
      接下来给大家举个例子:
	//正数
	int a = 23;
	//00000000000000000000000000010111	- 原码
	//00000000000000000000000000010111	- 反码
	//00000000000000000000000000010111	- 补码
	//负数
	int b = -22;
	//10000000000000000000000000010111	- 原码
	//11111111111111111111111111111101000	- 反码(符号位不变,其他位按位取反)
	//11111111111111111111111111111100001	- 补码(=反码+1)

整数在内存中存储的是补码,计算时也使用补码
相信聪明的你一定学fei了。

2. 移位操作符

<1> “>>”
  1. 算数右移(右边丢弃,左边补原来的符号位)
  2. 逻辑右移(右边丢弃,左边直接补0)
    接下来开始验证当前编译器是算数右移还是逻辑右移?
int main()
{
	int a = -23;
	//10000000000000000000000000010111	- 原码
	//11111111111111111111111111101000	- 反码
	//11111111111111111111111111101001	- 补码
	//算数右移
	int b = a >> 1;
	//11111111111111111111111111110100	- 补码  右移一位得	
	//11111111111111111111111111110011	- 反码(=补码-1)
	//10000000000000000000000000001100	- 原码 -12
	printf("%d\n", a);	//-23
	printf("%d\n",b);	//-12
	return 0;
}
  • 若当前编译器采用逻辑右移,即往首位补0,则以下结果应为负数;

  • 因此可判断VS编译器为算数右移
    「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」_第2张图片

  • 对于以下结果,我们可以理解将正数右移一位可看为将其向下取整;但负数则不行。
    「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」_第3张图片

<2> “<<”
  • 相较于右移操作符,左移操作符更为简单,话不多说,我们直接上代码:
int main()
{
	int a = 9;
	//00000000000000000000000000000101
	int b = a << 1;
	//【00000000000000000000000000001010】
	printf("%d", b);	//18
	return 0;
}

「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」_第4张图片

  • 二进制
//原码就是2进制,整形,4个字节,32比特位。正数原码,反码,补码相同。负数反码由原码的符号位不变,其他位按位取反得到,然后反码+1就是补码。整数在内存中存放的是补码,计算的也是补码
//

位操作符—也是操作二进制位
原码
反码
补码

二、位操作符(操作数必须为整数)

1. ’ & ’ 按位与

  • 规则:对应(二进制)位 有0则为0,同时为1则为1;
  • 代码如下:
int main()
{
	int a = 6;
	//00000000000000000000000000000110	- 补码
	int b = -5;
	//10000000000000000000000000000101	-原
	//11111111111111111111111111111010	-反
	//11111111111111111111111111111011	-补
	int c = a & b;
	//00000000000000000000000000000110	- 补码
	//11111111111111111111111111111011	-补
	//00000000000000000000000000000010	-按位与 &
	printf("%d\n", c);	//  c=2
	return 0;
}
  • 结果测试正确「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」_第5张图片

2. ’ | ’ 按位或

  • 规则:对应(二进制)位有1则为1,两个同时位0则为0
  • 代码如下:
//按位或'|'
int main()
{
	int a = 6;
	//00000000000000000000000000000110	- 补码
	int b = -5;
	//10000000000000000000000000000101	-原
	//11111111111111111111111111111010	-反
	//11111111111111111111111111111011	-补
	int c = a | b;
 	//00000000000000000000000000000110	- a补
	//11111111111111111111111111111011	-b补
	//11111111111111111111111111111111	-补 - (按位或 |)
	//11111111111111111111111111111110	-反 -(补码-1)
	//10000000000000000000000000000001	-原 -(按位取反)
	printf("%d\n", c);	//-1
	return 0;
}
  • 结果测试正确「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」_第6张图片

3. ’ ^ ’ 按位异或

  • 规则:按(二进制)位异或 - 对应(二进制)位不同位1,相同为0;
  • 特点: 异或具有交换律,证明如下:
//a^a—>0
//a^0=a
//假设 a=3  b=5
// a	011
// b	101
// a^b	110
// ①a^b^a110	=5=b
//②a^a^b=b
//0^b=b=5
综上,具有交换率
由此得出异或是支持交换率的
  • 代码如下:
int main()
{
	int a = 6;
	//00000000000000000000000000000110	- 补
	int b = -5;
	//10000000000000000000000000000101	-原
	//11111111111111111111111111111010	-反
	//11111111111111111111111111111011	-补
	int c = a ^ b;
	//00000000000000000000000000000110	- a补
	//11111111111111111111111111111011	- b补
	//11111111111111111111111111111101	-补 
	//11111111111111111111111111111100	-反 -(补码-1)
	//10000000000000000000000000000011	-原 -(按位异或)
	printf("%d\n", c);	//c = -3
	return 0;
}
  • 结果测试正确「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」_第7张图片

三、应用

1. 不创建临时变量,实现两数交换

int main()
{
	int a = 3;
	int b = 7;
	//1.创建临时变量
	/*int temp = a;
	a = b;
	b = temp;*/
	//2.不创建临时变量
	/*a = a + b;
	b = a - b;
	a = a - b;*/	
	//通过计算可以得到两数交换的值,可以赋值来思考下实现过程
	//但这种交换是有bug的,当值超过表示最大值范围时,会发生数据截断,导致数据错误;
	//3.优化终极版本——按位异或实现
	a = a ^ b;
	b = a ^ b;	//此处a等价于a^b,等式等价于b=a^b^b=a
	a = a ^ b;	//此处b为a,a为a^b,等式等价为a=a^b^a=b
				    //该交换未进行【进位】,故不会产生值溢出!
				    //虽然巧妙但缺点在于可读性不高,以后还是使用临时变量swap
	printf("a=%d b=%d\n", a, b);
}

2. 求一个整数二进制数中1的个数

步骤如下:

  1. 整数有32个比特位
  2. 获取该数二进制的每一位
    • 对该整数按位与1获取每一位(若不明白可返回上文查看按位与相关概念)
    • 之后进行按位右移让二进制序列动起来
  3. 判断是否为1
  4. 是1则计数器+1
    实现代码如下:
//求一个整数二进制数中1的个数
int main()
{
	int a = 0;	//整数
	int b = 1;
	int i = 0;	//循环变量
	int count = 0;
	scanf("%d", &a);   //7
	for (i = 0; i < 32; i++)
	{
		int c = a & b;
		a = a >> 1;
		if ( c== 1)
		{
			count++;
		}
	}
	printf("%d\n", count);  //3
	return 0;
}

「玩转C语言!从移位操作符和位操作符开始,让你的代码更加高效!」_第8张图片

感谢阅读!创作不易,如果觉得其中有能帮助到您,还请关注我,一起进步。
Live long and Prosper!

你可能感兴趣的:(C,c语言,算法,开发语言)