C语言——二进制/移位操作符/位操作符_学习笔记

二进制

二进制相关概念知识

二进制可以说是专门为了计算机而设计的一种数字系统,在该系统中,只有两个数字,0和1,进位规则是“逢二进一”。

二进制对计算机来说还是很有意义的,计算机是通过一系列硬件通过电子电路连接来进行相关计算的。

在计算机电路中,二进制数通常使用逻辑信号表示,即电路中只有两种状态:高电平和低电平。其中,高电平表示二进制数中的1,而低电平表示二进制数中的0。

二进制转十进制

二进制每一位的权重,从右向左依次是:2的0次方,2的1次方,2的2次方…以此类推。
比如:二进制的1101,转换成十进制为13
C语言——二进制/移位操作符/位操作符_学习笔记_第1张图片

十进制转二进制

十进制整数转换为二进制整数采用"除2取余,逆序排列"法。

具体做法是:用2整除十进制整数,可以得到一个商和余数;再用2去除商,又会得到一个商和余数,如此进行,直到商为小于1时为止,然后把先得到的余数作为二进制数的低位有效位,后得到的余数作为二进制数的高位有效位,依次排列起来。
C语言——二进制/移位操作符/位操作符_学习笔记_第2张图片

原码、反码、补码

整数的二进制表示方法有三种,原码、反码和补码。

三种表示方法都有符号位和数值位这两部分,2进制序列中,最高位的1位是符号位,其余的为数值
位。其中,符号位若为0,则表示该数为正数;符号位为1,表示该数是负数;

正整数的原码、反码和补码都相同。

负整数的例外,负整数的三种码的表示方法各不相同:
原码:符号位为1,数值位和正数的数值位一样。(5和-5的数值位相同,-5的符号位为1,5的符号位为0)
反码:将原码的符号位不变,数值位依次按位取反得到反码。
补码:反码+1 得到补码。
举例:(C语言中,VS2020环境下,一个int类型,数值-5,二进制相关表示如下)
C语言——二进制/移位操作符/位操作符_学习笔记_第3张图片
对于整形来说,数据是以补码的形式存储在内存中的

这里拓展一下知识,为什么在计算机中,数值一律用补码来表示和存储
原因:使用补码,可以将符号位和数值位进行统一处理;同时,加法和减法也可以统一处理(CPU只有加法器)此外,补码和原码相互转换,其运算过程是相同的,不需要设计额外的硬件电路就能实现。

移位操作符(移位操作符的操作数只能是整数)

<<左移操作符(左边丢弃,右边补0)

C语言——二进制/移位操作符/位操作符_学习笔记_第4张图片

>>右移操作符(右移运算分为两种)

1. 逻辑右移:左边用0填充,右边丢弃

C语言——二进制/移位操作符/位操作符_学习笔记_第5张图片

2. 算术右移:左边用原该值的符号位填充,右边丢弃

C语言——二进制/移位操作符/位操作符_学习笔记_第6张图片

PS:对于移位操作符,不要移动负数位(比如3 <<-1或者3 >> -1),这个是标准未定义的。
PS:一般逻辑右移比较常见,VS2020环境下右移操作也是逻辑右移。

移位操作符的应用

  1. 快速计算幂次
    一个数左移几位,相当于将其乘以2的几次方
    一个数右移几位,相当于将其除以2的几次方
int num = 10;
# 10乘以23次方
result = x << 3

# 10除以2的n次方
result = x >> 3
  1. 移位操作符与位操作符连用的应用场景比较多。

位操作符

“&” 按位与

对于两个相应的二进制位,只有当两个位都是1时,结果才是1,否则为0。

//例如:5 & 8 = 0
00000000 00000000 00000000 00000101 //5的补码(正整数的原码、反码、补码都一样)
00000000 00000000 00000000 00001000 //8的补码
————————————————————————————————————————————————————————————————————————————————
00000000 00000000 00000000 00000000 //结果为0

“|” 按位或

对于两个相应的二进制位,只要其中一个是1,结果就是1,只有当两个位都是0时,结果才是0。

//例如:5 | 8 = 13
00000000 00000000 00000000 00000101 //5的补码(正整数的原码、反码、补码都一样)
00000000 00000000 00000000 00001000 //8的补码
————————————————————————————————————————————————————————————————————————————————
00000000 00000000 00000000 00001101 //结果为13

“^” 按位或

对于两个相应的二进制位,当两个位不同时,结果为1,当两个位相同时,结果为0。

//例如:5 ^ 8 = 13
00000000 00000000 00000000 00000101 //5的补码(正整数的原码、反码、补码都一样)
00000000 00000000 00000000 00001000 //8的补码
————————————————————————————————————————————————————————————————————————————————
00000000 00000000 00000000 00001101 //结果为13

“~” 按位取反

它翻转操作数的每一位(包括符号位)

//例如:~ 5 = -6
00000000 00000000 00000000 00000101 //5的补码(正整数的原码、反码、补码都一样)
————————————————————————————————————————————————————————————————————————————————
11111111 11111111 11111111 11111010 //一个负数的补码
10000000 00000000 00000000 00000101 //反码
10000000 00000000 00000000 00000110 //原码,结果为-6

位操作符的妙用

  1. 不能创建临时变量(即不能使用第三个变量),实现两个数的交换
#include 
int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:a = %d b = %d\n", a, b);

	a = a ^ b;
	b = a ^ b;
	a = a ^ b;
	printf("交换后:a = %d b = %d\n", a, b);
	return 0;
}

输出结果:

交换前:a = 10 b = 20
交换后:a = 20 b = 10
  1. 求⼀个整数存储在内存中的⼆进制中1的个数。
#include 
int main()
{
	int num = -1;
	int i = 0;
	int count = 0;//计数
	while (num)
	{
		count++;
		num = num & (num - 1);
	}
	printf("二进制中1的个数 = %d\n", count);
	return 0;
}

输出结果:

二进制中1的个数 = 32
  1. 在一个整型数组中,只有一个数字出现一次,其他数组都是成对出现的,找出那个只出现一次的数字。
    例如:
    数组中有:1 2 3 4 5 1 2 3 4,只有5出现一次,其他数字都出现2次,找出5
#include 

int find_single_dog(int arr[], int sz)
{
    int ret = 0;
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        ret ^= arr[i];
    }
    return ret;
}
int main()
{
    int arr[] = { 1,2,3,4,5,1,2,3,4 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    int dog = find_single_dog(arr, sz);
    printf("%d\n", dog);


    return 0;
}

输出结果

5

知识点积累

1. n&(n-1)

这行代码运行一次,n的二进制补码中最右边的1就消失了。

也就是说,这行代码运行了几次,就说明n的二进制补码中有几个1。代码如下

#include 
int main()
{
	int n = 0;
	scanf("%d", &n);
	int count = 0;
	while (n)
	{
		n = n & (n - 1);
		count++;
	}
	printf("%d", count);
}

这个表达式还可以用来判断n是不是2的次方。代码如下:

#include 
int main()
{
	int n = 0;
	scanf("%d", &n);
	if ((n & (n - 1) )== 0)
	{
		printf("YES\n");
	}
	else
	{
		printf("NO\n");
	}
}

你可能感兴趣的:(C语言学习笔记,c语言,学习,笔记)