C语言之详解位操作符和移位操作符

本文详细介绍了C语言中的位操作符和移位操作,包括二进制、八进制、十六进制之间的转换,以及原码、反码、补码的概念。还通过实例展示了如何在不使用临时变量的情况下交换两个数,以及如何计算整数中1的个数和二进制位的不同数量。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 二进制和进制转换

生活我们使用的都是十进制数。其实,除了十进制外,还有二进制、八进制、十六进制,而计算机中使用的就是二进制。不管是什么进制,其实就是一个数的不同的表示形式。
例如:

13的二进制是:1101
13的八进制是:15
13的十进制是:13
13的十六进制是:D

十进制的特征:

  1. 逢10进1(满10进1)
  2. 10进制数字的每一位都是由0~9的数字组成

二进制的特征:

  1. 逢2进1(满2进1)
  2. 二进制数字的每一位都是由0~1的数字组成

八进制,十六进制也是如此。其中,16进制中每一位数字如果是10-15,用A-F表示。

1.1 二进制、八进制、十六进制数字转十进制数字

十进制数字123是为什么是表示一百二十三呢?其实是这样得到的:
1 * 10^2 + 2 * 10^1 + 3 * 10^0 = 123

二进制数字1101为什么表示十三呢?同样地:
1 * 2^3 +1 * 2^2 + 0 * 2^1 + 1 * 2^0 = 13
八进制,十六进制亦是如此,由此能得到我们平常常用的十进制数字。

1.2 十进制数字转二进制数字

方法:运用短除法,不断用2去除十进制数字,除至1或0
在这里插入图片描述
所以,13用二进制表示是1101

1.3 二进制转八进制、十六进制

二进制转八进制方法:
从2进制序列中最右边低位开始向左每3位2进制位,将其换算成一个八进制位,剩余不够的3个二进制位的直接换算

例如:二进制的1101011,换算成八进制:0153
注: 0开头的数字,会被当成八进制
在这里插入图片描述
二进制转十六进制方法:从2进制序列中最右边低位开始向左每4位2进制位,将其换算成一个十六进制位,剩余不够的4个二进制位的直接换算

如:二进制的1101011,换算成十六进制:0x6b
注: 十六进制表示的时候前面要加0x(或0X)
在这里插入图片描述

1.4 八进制、十六进制转二进制

八进制、十六进制转二进制需要先将八进制、十六进制数字转化成十进制数字,再按照十进制转二进制的方法,将其转化成二进制数字

2. 原码、反码、补码

整数的二进制表示方法有3种,即原码、反码、补码

有符号整数的三种表示方法均有符号位和数值位两部分,二进制序列种,最高位被当成符号位,剩余的都是数值位
符号位是由0表示正,1表示负

那么如何得到原码、反码、补码呢?

原码就是整数用二进制表示的序列,其中最高位表示符号
例如: 在8位的情况下:-13的原码为10001101

正整数的原码、反码、补码均相同
负数的反码,是将原码除符号位以外,将数值位都按位取反,(即将1改为0,0改为1
例如: 将上面的原码变为反码为:11110010
负数的补码,是将反码 +1
例如: 将上面的反码变为补码:11110011

那么如何由补码得到原码呢?
有两种方法:

  • 先将补码 -1,再按位取反
  • 先将补码按位取反,再 +1

注: 对于整形来说,数据在内存中存储的其实是补码

3. 移位操作符

移位操作符有两种:<< 左移操作符,>> 右移操作符
注意:

  1. 移位操作符的操作数必须是正整数 ,不能是负数,这是未定义的
  2. 移位操作的是这个数的补码,最终如果要输出,再根据操作后的补码求得原码,最后输出

3.1 左移操作符

移位规则:左边抛弃,右边补0
例如:

#include <stdio.h>

int main()
{
	int num = 10;
	printf("移动前:%d\n", num);
	printf("移动后:%d\n", num << 1);
	return 0;
}

运行结果如下:
在这里插入图片描述

原因如下:
在这里插入图片描述
补充: 左移操作符有乘2的效果,每左移一位,就乘一个2

3.2 右移操作符

移位规则:

  • 逻辑右移:右边抛弃,左边补0
  • 算术右移:右边抛弃,左边用原数字的符号位补

那我们在使用右移操作符时,到底该遵从上面哪一个规则呢?答案是取决于编译器,常见的编译器都是算术右移

举个例子:

#include <stdio.h>

int main()
{
	int num = 10;
	printf("移动前:%d\n", num);
	printf("移动后:%d\n", num >> 1);
	return 0;
}

运行结果如下:
在这里插入图片描述
原理如下:VS2022遵从的是算术右移

补充:

  1. 大部分情况下,右移1位,有除2的效果
  2. -1的补码每一位全是1,且不管算术右移多少位,其补码始终是-1,因此原码也始终是-1

4. 位操作符

注意:

  1. 位操作符的操作数必须是整数!!!
  2. 位操作符操作的是补码,得到的也是补码,如果需要输出,则需要将得到的补码转化为原码,再将原码对应的十进制数输出

4.1 按位与 &

运算规则:有0则为0,都为1才为1
我们之前有学过逻辑与&&,只有全为真,整体才成立。而在C语言中,用0表示假,非0表示真,因此,对于按位与的运算规则,我们可以类似于逻辑与
,这样可以帮助我们记忆。

4.2 按位或 |

运算规则:有1则为1,都为0才为0
同样的,我们可以类似于逻辑或||,帮助我们记忆。

4.3 按位异或 ^

运算规则:相同为0,相异为1

4.4 按位取反

运算规则:将1改为0,将0改为1

5. 实际应用

5.1 一道变态的面试题:不能创建临时变量(第三个变量),实现两个数的交换

补充知识:a ^ a = 0,a ^ 0 = a

#include <stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	printf("交换前:%d %d\n", a, b);
	a = a ^ b;
	b = a ^ b;// b = (a ^ b) ^ b = a ^ (b ^ b) = a 
	a = a ^ b;// a = (a ^ b) ^ a = a ^ a ^ b = b
	printf("交换后:%d% d\n", a, b);
}

在这里插入图片描述
补充知识:a ^ a = 0,a ^ 0 = a

5.2 编写代码实现:求一个整数存储在内存中的二进制中1的个数

首先,我们要弄清楚整数存储在内存中是什么,答案是补码。因此,这道题就是求一个数的补码中1的个数

#include <stdio.h>

int main()
{
	int num = 0;
	printf("请输入一个整数:");
	scanf("%d", &num);
	int count = 0;
	while (num)
	{
		num = num & (num - 1);
		count++;
	}
	printf("补码中1的个数为:%d\n", count);
}

在这里插入图片描述

5.3 编写代码实现:输入两个整数,求两个整数二进制格式有多少个位不同

OJ链接:牛客网的OJ链接(超链接)

#include <stdio.h>

int main()
{
    int m = 0;
    int n = 0;
    scanf("%d%d", &m, &n);
    int count = 0;
    int ret = m ^ n;
    while (ret)
    {
        ret = ret & (ret - 1);
        count++;
    }
    printf("%d\n", count);
    return 0;
}

在这里插入图片描述


end

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值