位运算符及其相关操作详解

news2024/9/21 20:36:21

位运算符详解

前言:由于位运算符是直接对二进制数操作,因此对二进制、八进制、十六进制不甚了解的小伙伴建议先看这篇二进制、八进制、十六进制与十进制的相互关系,这样阅读本篇时将事半功倍

总览

  • 位运算是对计算机存储的二进制序列的相应位进行操作

  • 位运算的操作数必须是整数型或字符型

运算符示例
按位与 &a & b
按位或 |a | b
按位异或 ^a ^ b
按位取反 ~~ a
左移 <<a << b
右移 >>a >> b

按位与 &

  • 按位与是指:参加运算的两个操作数,按二进制对应的位进行“与”运算。如果两个相应的二进制位都为1,则得到的结果为1,否则位0。

  • 0 & 0 = 0,0 & 1 = 0,1 & 1 = 1,1 & 0 = 0,即 全一为1,有零则零

    	int a = 15;
    	//15的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1111
    	int b = 17;
    	//16的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001
    	int c = a & b;
    	//a和b按位与,那么得到的二进制序列应该是 0000 0000 0000 0000 0000 0000 0000 0001
    	//即十进制的1
    	printf("c = %d\n", c);
    

    在这里插入图片描述

按位或 |

  • 两个相应的二进制位中只要有一个为1,则得到的结果为1。

  • 0 | 0 = 0,0 | 1 = 1,1 | 0 = 1,1 | 1 = 1,即 有一为1,全零为0

    int a = 15;
    //15的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1111
    int b = 17;
    //16的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001
    int c = a | b;
    //a和b按位与,那么得到的二进制序列应该是 0000 0000 0000 0000 0000 0000 0001 1111
    //即十进制的31
    printf("c = %d\n", c);
    

    在这里插入图片描述

按位异或 ^

  • 参加运算的两个二进制位,如果相同则为0,不同则为1

  • 1 ^ 1 = 0,1 ^ 0 = 1,0 ^ 1 = 1,0 ^ 0 = 0,即 不同为1,相同为0

    int a = 15;
    //15的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1111
    int b = 17;
    //16的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001
    int c = a ^ b;
    //a和b按位与,那么得到的二进制序列应该是 0000 0000 0000 0000 0000 0000 0001 1110
    //即十进制的30
    printf("c = %d\n", c);
    

    在这里插入图片描述

按位取反 ~

  • 将二进制中的每个位的 0变成1,1变成0

  • 如 ~025就是对八进制数25(即二进制数000 010 101)进行按位取反,得到752(111 101 010)

    int a = 15;
    //15的二进制序列为 0000 0000 0000 0000 0000 0000 0000 1111
    int b = ~ a;
    //a按位取反,二进制序列为 1111 1111 1111 1111 1111 1111 1111 0000
    // 由于在计算机内部是按补码储存,那么转换为反码 1111 1111 1111 1111 1111 1111 1110 1111
    // 转化为原码 1000 0000 0000 0000 0000 0000 0001 0000
    //即十进制的-16
    printf("b = %d\n", b);
    
    int c = -1;
    //-1的二进制序列为 1111 1111 1111 1111 1111 1111 1111 1111
    int d = ~c;
    //c按位取反,二进制序列为 0000 0000 0000 0000 0000 0000 0000 0000
    //即十进制的0
    printf("d = %d\n", d);
    

    在这里插入图片描述

左移 <<

  • 左移运算符是用来将一个数的各二进制位全部向左移动若干位,右边的空位补零

  • 例如 a = 3(10) = 011(2),那么 a << 1的结果就是110(2)=6(10)

    int a = 17;
    //a的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001
    int b = a << 2;
    //b为所有二进制为左移两位得到 0000 0000 0000 0000 0000 0000 0100 0100
    //即十进制数 68
    printf("b = %d\n", b);
    

    在这里插入图片描述

右移 >>

  • 右移运算符是用来将一个数的各二进制位全部向右移动若干位
  • 逻辑右移:右移之后的空位无论原符号位是什么,统一补零
  • 算术右移:如果原符号位位1,那么空位全部补1,反之如果符号位为0,那么空位补0
  • 一般来说,编译器采用的是算术右移
	int a = 17;
	//a的二进制序列为 0000 0000 0000 0000 0000 0000 0001 0001
	int b = a >> 2;
	//b为所有二进制为左移两位得到 0000 0000 0000 0000 0000 0000 0000 0100
	//即十进制数4
	int c = -15;
	//c的二进制原码序列为 1000 0000 0000 0000 0000 0000 0000 1111
	//反码 1111 1111 1111 1111 1111 1111 1111 0000
	//补码 1111 1111 1111 1111 1111 1111 1111 0001
	int d = c >> 2;
	//如果是逻辑右移
	//d的二进制序列为 0011 1111 1111 1111 11111 1111 1111 1100
	//如果是算术右移
	//d的二进制序列为 1111 1111 1111 1111 1111 1111 1111 1100
	//反码 1111 1111 1111 1111 1111 1111 1111 1011
	//原码 1000 0000 0000 0000 0000 0000 0000 0100
	//即十进制数-4
	printf("b = %d\n", b);
	printf("d = %d\n", d);

在这里插入图片描述

位运算的运用

判断一个数是否为偶数

  • Tips:利用位运算对一个数进行奇偶判断的速度要快于if(num % 2 == 0){……}这一操作

  • 我们知道二进制数每一位的权重为2,因此要判断一个数是否为偶数,只需要判断它二进制位的第一位是1还是0

    • 如果第一位是1,那么十进制值就会加上20 = 1这个奇数,而偶数加奇数一定为奇数,因此该数一定是奇数
    • 如果第一位是0,那么这个1就不会加上,该数就一定是偶数
  • 那我们如何得到二进制数的第一位呢?将这个数和1进行按位与操作就可以了,因为1的二进制位只有第一位为1,这样我们就可以单独对二进制的第一位进行判断,从而判断该数的奇偶性

    #include<stdio.h>
    int main()
    {
    	int a;
    	while (scanf_s("%d", &a) != EOF)
    	{
            //注意:关系运算符的优先级高于&,|,^操作符
    		if ((a & 1) == 0)
    			printf("%d是偶数\n",a);
    		else
    			printf("%d是奇数\n", a);
    	}
    	return 0;
    }
    

    在这里插入图片描述

统计二进制数中1的个数

方法一

  • 我们知道如果我们要获得十进制数的每一位,我们可以对这个数不断进行模10除以10操作

    • 例如要获得十进制数139的每一位,139 % 10 = 9,得到各位9,139 / 10 = 13,13 % 10 = 3,得到十位3,13 / 10 = 1,1 % 10 = 1,得到百位1
  • 那么对二进制数也可以这样操作,只是将模10除以10改为模2除以2

    int One_Count(unsigned int num)	//参数定义为无符号型是为了确保对负数运算的正确
    {
    	int count = 0;
    	while (num)
    	{
    		if (num % 2 == 1)
    			count++;
    		num /= 2;
    	}
    	return count;
    }
    

方法二

  • 可以利用 & 获得二进制的每一位

    int One_Count(int num)
    {
    	int count = 0;
    	for (int i = 0; i < 32; i++)
    	{
    		if (((num >> i) & 1) == 1)
    			count++;
    	}
    	return count;
    }
    

方法三(巧解)

  • 可能有小伙伴会认为上面的方法二中的循环固定要循环32次,当计算小数字二进制中1的个数时会浪费时间,那么还有没有更加极致的方法呢?

  • 有!我们来看一个表达式:n = n & (n - 1)

    • 举个例子,n = 15(10) = 1111(2), n - 1 = 14(10) = 1110(2), n = n & (n-1) = 1110(2)

      ​ n = 1110(2), n - 1 = 1101(2), n = n & (n-1) = 1100(2)

      ​ n = 1100(2), n - 1 = 1011(2), n = n & (n-1) = 1000(2)

      ​ n = 1000(2), n - 1 = 0111(2), n = n & (n-1) = 0000(2)

  • 我们可以发现,这个表达式每一次计算,都可以将数n二进制位中最右边的1变成0,因此我们可以创建一个循环,n = n & (n - 1)这个表达式可以运行的次数就是数n二进制位中含1的个数

int One_Count(int num)
{
	int count = 0;
	while (num)
	{
		num = num & (num - 1);
		count++;
	}
	return count;
}

统计两个二进制数中不同位的个数

  • 根据按位异或 ^ 的规律,两个二进制位如果不同,则结果为1,相同结果为0,因此我们可以将这两个数先按位异或,在统计结果二进制位中1的个数

    #include<stdio.h>
    int One_Count(int num)
    {
    	int count = 0;
    	while (num)
    	{
    		num = num & (num - 1);
    		count++;
    	}
    	return count;
    }
    int main()
    {
    	int a, b, c;
    	scanf_s("%d %d", &a, &b);
    	c = a ^ b;
    	printf("%d和%d二进制不同位有%d个\n", a,b,One_Count(c));
    	return 0;
    }
    

将一个数用二进制形式打印(左边为高位,右边为地位)

#include<stdio.h>
void Print(int num)
{
	unsigned int temp = 0x80000000;
   //即二进制数 1000 0000 0000 0000 0000 0000 0000 0000
   //第一位不看成符号位
	while (temp)
	{
		if ((num & temp))
			printf("1 ");
		else
			printf("0 ");
		temp >>= 1;
	}
}
int main()
{
	int a;
	scanf_s("%d", &a);
	Print(a);
	printf("\n");
	return 0;
}

不创建临时变量交换两个数

  • 这里我们要用到按位异或 ^

  • 这种方法的运行效率不如创建临时变量

    void exchange(int *a, int *b)
    {
        int temp = *a;
        *a = *b;
        *b = temp;
    }
    
  • 这种方法仅作了解即可

    #include<stdio.h>
    int main()
    {
    	int a = 2, b = 3;
    	a = a ^ b;
    	b = a ^ b;
    	a = a ^ b;
    	return 0;
    }
    

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/547634.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【笔试强训选择题】Day17.习题(错题)解析

作者简介&#xff1a;大家好&#xff0c;我是未央&#xff1b; 博客首页&#xff1a;未央.303 系列专栏&#xff1a;笔试强训选择题 每日一句&#xff1a;人的一生&#xff0c;可以有所作为的时机只有一次&#xff0c;那就是现在&#xff01;&#xff01;&#xff01; 前言 目…

南京邮电大学算法与设计实验三:动态规划法(最全最新,与题目要求一致)

实验原理&#xff1a; 1、用动态规划法和备忘录方法实现求两序列的最长公共子序列问题。要求掌握动态规划法思想在实际中的应用&#xff0c;分析最长公共子序列的问题特征&#xff0c;选择算法策略并设计具体算法&#xff0c;编程实现两输入序列的比较&#xff0c;并输出它们的…

Linux之NetLink学习笔记

1.NetLink机制 NetLink是一种基于应用层跟内核态的通信机制&#xff0c;其特点是一种异步全双工的通信方式&#xff0c;支持内核态主动发起通信的机制。该机制提供了一组特殊的API接口,用户态则通过socket API调用。内核发送的数据再应用层接收后会保存在接收进程socket的缓存…

MediaPipe虹膜检测:实时虹膜跟踪和深度估计

包括计算摄影(例如,人像模式和闪光反射)和增强现实效果(例如,虚拟化身)在内的大量实际应用都依赖于通过跟踪虹膜来估计眼睛位置。一旦获得了准确的光圈跟踪,我们就可以确定从相机到用户的公制距离,而无需使用专用的深度传感器。反过来,这可以改善各种用例,从计算摄影…

《Kali渗透基础》01. 介绍

kali渗透 1&#xff1a;渗透测试1.1&#xff1a;安全问题的根源1.2&#xff1a;安全目标1.3&#xff1a;渗透测试1.4&#xff1a;标准 2&#xff1a;Kali2.1&#xff1a;介绍2.2&#xff1a;策略2.3&#xff1a;安装 3&#xff1a;Kali 初步设置3.1&#xff1a;远程连接3.1.1&a…

深度学习之全过程搭建卷积神经网络(CNN)

大家好&#xff0c;我是带我去滑雪&#xff01; 本期将尝试使用CIFAR-10 数据集搭建卷积神经网络&#xff0c;该数据集由 10 个类别的 60000 张 32x32 彩色图像组成&#xff0c;每个类别有 6000 张图像。 下面开始全过程搭建CNN识别彩色图片&#xff1a; 目录 &#xff08;1&a…

【Linux】冯诺依曼与操作系统

目录 一、冯诺依曼结构体系1、冯诺依曼结构体系简介2、为什么要有内存呢&#xff1f; 二、操作系统1、操作系统如何对硬件进行管理&#xff1f;2、操作系统为什么要对软硬件进行管理&#xff1f; 一、冯诺依曼结构体系 1、冯诺依曼结构体系简介 在现实生活中&#xff0c;我们…

KEYSIGHT MSOS204A 2GHZ 4通道DSOS204A高清晰度示波器

KEYSIGHT是德DSOS204A/MSOS204A高清晰度示波器 附加功能&#xff1a; 2 GHz 带宽&#xff08;可升级&#xff09; 4 个模拟通道和 16 个数字通道 最大存储深度&#xff1a;800 Mpts&#xff08;2 通道&#xff09;&#xff0c;400 Mpts&#xff08;4 通道&#xff09; 最大…

菱形继承、菱形虚拟继承、以及菱形虚拟继承的模型结构内部。

1. 单继承&#xff1a;一个子类只有一个直接父类。 多继承&#xff1a;一个子类有两个或以上直接父类。 菱形继承&#xff1a;菱形继承是多继承的一种特殊情况。 下面是代码和对象模型结构&#xff0c;可以看出菱形结构存在哪些问题&#xff0c;如下&#xff1a; #define _CR…

学习经验分享【30】Pycharm插件chatgpt,用来辅助编写代码

在Pycharm中发现ChatGPT插件&#xff0c;很好用&#xff0c;免费安全&#xff0c;大家可以作为编代码的辅助工作&#xff0c;也可用来玩GPT的接口。具体方法如下 实现效果如下&#xff1a; 更多精彩内容敬请持续关注。如果本博文对你有帮助的话&#xff0c;欢迎点赞、评论区留言…

BUUCTF-一叶障目 解析

打开文件发现一张png图片&#xff0c;里面没有内容&#xff0c;使用tweakpng打开 tweakpng报错 &#xff0c;说明crc校验值对不上 有两种可能&#xff0c;一是crc值被修改&#xff0c;二是图片的宽高被修改&#xff08;在ctf中多半是后者&#xff09; 先尝试修改crc值为55900…

【王道·计算机网络】第五章 传输层

一、传输层概述 传输层为应用层提供通信服务&#xff0c;使用网络层服务传输层的功能&#xff1a; 提供进程和进程之间的逻辑通信&#xff08;网络层提供主机之间的逻辑通信&#xff09;复用&#xff08;发送发不同的应用进程&#xff09;和分用&#xff08;接收方正确的数据传…

【网络协议详解】——PPP协议(学习笔记)

目录 &#x1f552; 1. 数据链路层协议概述&#x1f552; 2. PPP协议分析&#x1f558; 2.1 概述&#x1f558; 2.2 工作流程&#x1f558; 2.3 帧格式 &#x1f552; 3. LCP协议&#x1f558; 3.1 概述&#x1f558; 3.2 报文格式&#x1f558; 3.3 报文种类&#x1f564; 3.3…

3年经验,面试测试岗只会功能测试开口要求18K,令我陷入沉思

由于朋友临时有事&#xff0c; 所以今天我代替朋友进行一次面试&#xff0c;公司需要招聘一位自动化测试工程师&#xff0c;我以很认真负责的态度完成这个过程&#xff0c; 大概近30分钟。 主要是技术面试&#xff0c; 在近30分钟内&#xff0c; 我与被面试者是以交流学习的方式…

STM32F407+LWIP+DP83848以太网驱动移植

最近有个项目上需要用到网络功能&#xff0c;于是开始移植网络相关代码。在移植的过程中感觉好难&#xff0c;网上找各种资料都没有和自己项目符合的&#xff0c;移植废了废了好的大劲。不过现在回头看看&#xff0c;其实移植很简单&#xff0c;主要是当时刚开始接触网络&#…

【数据分享】2020年我国地级市医疗资源空间分布数据(Shp格式/Excel格式)

医疗资源的配置情况直接反映了一个城市的发展水平&#xff0c;医疗资源相关数据也是经常使用到的数据&#xff01; 我们发现学者刘海猛在科学数据银行&#xff08;ScienceDB&#xff09;平台上分享了2020年我国341个城市&#xff08;地区、州、盟&#xff09;的基础医疗资源数…

电脑安装软件时,如何避免捆绑安装?

在网络上非正规网站下载安装软件时&#xff0c;经常会遇到捆绑安装的情况。你明明下载了一个软件&#xff0c;电脑上却多出好几个。那么我们在安装软件时&#xff0c;如何才能避免捆绑安装呢&#xff1f; 什么是捆绑安装&#xff1f; 捆绑安装是指用户安装一个软件时&#xff…

Spring boot框架 JWT实现用户账户密码登录验证

目录 1、JWT定义 1、1 JWT工作流程 1、2 JWT优点 2、添加依赖项到pom.xml 3、创建用户实体类 4、实现认证服务 5、登录请求处理 6、生成JWT 1、JWT定义 JWT&#xff08;JSON Web Token&#xff09;是一种用于在网络应用间传递信息的安全传输方式。它是一种紧凑且自包含…

tolua源码分析(五)lua使用C#的enum

tolua源码分析&#xff08;五&#xff09;lua使用C#的enum 上一节我们讨论了C#类是如何注册到lua的过程&#xff0c;以及lua调用C#函数时底层所做的事情。在此基础之上&#xff0c;本节我们来看看C#的enum是如何注册到lua的&#xff0c;它和一般类的注册有哪些区别。 老规矩&a…

互联网医院资质代办|互联网医院牌照的申请流程

随着互联网技术的不断发展&#xff0c;互联网医疗已经逐渐成为人们关注的热点话题。而互联网医院作为互联网医疗的一种重要形式&#xff0c;也越来越受到社会各界的关注。若想开展互联网医院业务&#xff0c;则需要具备互联网医院牌照。那么互联网医院牌照的申请流程和需要的资…