C++新经典04--位运算

news2024/11/17 3:38:18

背景

许多网络游戏为了刺激玩家每天上线,都在游戏中设有“每日任务”——每天让玩家做一些任务,如杀怪、采集来赚取积分、金钱、经验等。每日任务根据游戏不同,数量也不同,每日任务比较少的网络游戏中,可能每日任务只有几个,每日任务多的网络游戏,可能每日任务有几十个。
现在假设要做一款网络游戏,其中每日任务有10个,例如给好友送一次礼物、花金币购买一次物品、和其他玩家进行一次PK、杀死一个游戏中的怪等,都属于每日任务之一。现在的需求是:记录这10个任务是否完成,没完成的,用0表示,完成的,用1表示。

可能读者很快就能想到一个解决方法:定义10个变量,或者定义10个元素的整型数组,每个数组元素存一个任务标记0或者1:
在这里插入图片描述
试想,如果往数据库中记录该玩家的每日任务是否完成,是要记录10条数据,这显然很浪费数据库的存储空间。针对这个问题,有两个前提条件先约定一下:
· 每日任务只有10个。
· 只需记录该任务是否完成:0(未完成)或者1(已完成)这两个状态之一。

由此,就想到了位运算。一个unsigned int型数据有4字节,也就是32个二进制位,每个二进制位又都可以是0或者1,这样看来,只需要用一个unsigned int型变量,就可以记录多达32种状态,每个状态要么是0,要么是1。也就是说,其实只需要用一个unsigned int型变量,就能记录下每日这10个任务是否完成,甚至一个unsignedint型变量能记录多达32个每日任务是否完成(因为有32个二进制位),远远超过10个每日任务这个数量,这样,往数据库中保存玩家每日任务数据时,就只需要记录一条数据。

一个unsignedint型数据,一共32个位,最右边代表第1位,逐渐往左边来,最左边代表第32位,最左边的称为最高位,最右边的称为最低位,如图11.1所示。
在这里插入图片描述
现在每日任务只有10个,那么只需要使用其中的10位表示这10个任务是否做完就可以了,用0表示任务没做完,用1表示任务已经做完,用最右边的最低位表示第1个任务,然后依次往左,表示第2个任务,第3个任务……,一直到第10个任务,如图11.2所示。
在这里插入图片描述
这里需要写一些代码来做一些基本操作,要实现两个功能:
· 判断某个任务是否做完。
· 标记某个任务已经做完了。

下面要讲解一些前提代码,请注意看下面的代码:

#define BIT(x) (1 <<(x))
//这段代码日后的工作中都有实用价值,
//#define曾经讲过,是带参数的宏定义

分析一下这个带参数的宏定义,会得到如下结果:
· BIT(0)等价于(1≪(0)),代表1左移0位;
· BIT(1)等价于(1≪(1)),代表1左移1位;
· BIT(2)等价于(1≪(2)),代表1左移2位。

每左移一位相当于乘以2,所以,上面这个#define的功能就能够推测出来:
在这里插入图片描述
所以执行下面这段代码:

   int i;
    for(i=0;i<10;i++)
    {
	    printf("BIT(%d)=%d\n",i,BIT(i));	//1,2,4,8,16,32,…,512
    }

结果如图11.3所示。

BIT(0)=1
BIT(1)=2
BIT(2)=4
BIT(3)=8
BIT(4)=16
BIT(5)=32
BIT(6)=64
BIT(7)=128
BIT(8)=256
BIT(9)=512

图11.3

看一下结果数字,这些数字是1、2、4、8、16、32、64、128、256、512,从表面看,可以观察到,每个数字都是前面的数字*2得到的。现在把这些数字变成二进制数再观察一下:
在这里插入图片描述
有了上面这些知识,就能判断某个任务是否做完了。定义一个无符号整型变量如下:

unsigned int task;

注意,这里给这个任务变量取名叫task,task变量一共32位长;如果想看第7个任务是否做完,怎么看呢?如果第7个位置是1,就说明第7个任务做完了;如果第7个位置是0,就说明第7个任务没做完,如图11.4所示。
在这里插入图片描述
现在,问题的关键就是要把这第7个位置的数据提取出来。如何提取,就需要用到位运算。回忆一下上一节的按位与运算符“&”,如果两个相应的位都为1,则该位的结果为1,否则为0。回忆一下按位与的公式:
在这里插入图片描述
可以想象,如果把task与1000000(这是二进制数,第7位为1,其他位为0)做按位与运算,会出现什么结果?如果第7位为1,则结果肯定会如图11.5所示。
在这里插入图片描述
那么,得到的结果描述如下:如果task中(一共有32位)的第7位是0,那么task&1000000=0;如果第7位是1,那么task&1000000=1000000(二进制)=64(十进制)=BIT(6)。
所以,要判断某个任务是否做完,完整的判断代码应该这样写,这些代码具备商用价值,供读者参考和借鉴:

//10个任务
enum EnumTask
{
	ETask1 = BIT(0),	//1=1
	ETask2 = BIT(1),	//2=10
	ETask3 = BIT(2),	//4=100
	ETask4 = BIT(3),	//8=1000
	ETask5 = BIT(4),	//16=10000
	ETask6 = BIT(5), 	//32=100000
	ETask7 = BIT(6),	//64=1000000
	ETask8 = BIT(7),	//128=10000000
	ETask9 = BIT(8),	//256=100000000
	ETask10 = BIT(9),	//512=1000000000
}
unsigned int task = 0;
//刚开始所有任务都没执行过,所以任务变量先初始化为0
//判断第7个任务是否执行过了。按位与,不为0则表示任务7做过
if(task & ETask7)
{
	//任务7已经做过
	printf("任务7已经做过了\n");
}
else
{
	//任务7还没做过
	printf("任务7还没做过,现在做任务7\n");
}

以上就判断出任务7做没做,核心代码就是这一句:if(task&ETask7)。接着思考,如果任务7没做,如何把任务7做了,也就是让任务7这个位置标记上1?这就用到了按位或运算符“|”,参加运算的两个运算量,如果两个相应的位有一个为1,则该位的结果为1,否则为0。回忆一下按位或的公式:
在这里插入图片描述
所以,如果把task与1000000(这是二进制数,第7位为1,其他位为0)做按位或运算,会出现什么结果呢?结果就是其他位都不变,但是第7位肯定变为1(不管原来是什么);把第7位标记为1,就起到了标记任务7做完了的目的。所以代码继续完善如下:

    unsigned int task = 0;
    //刚开始所有任务都没执行过,所以任务变量先初始化为0
    //判断第7个任务是否执行过了
    if (task & ETask7)	//按位与,不为0则表示任务7做过
    {
        //任务7已经做过
        printf("任务7已经做过了\n");
    }
    else
    {
        //任务7还没做过
        printf("任务7还没做过,现在做任务7\n");
        //把任务7做了(标记任务7做完)
        task = task | ETask7;	//位操作运算符优先级高于赋值运算符
    }
    //再次判断任务7是否做过了
    if (task & ETask7)
    {
        printf("任务7已经做过了,可以把这个task变量值保存到数据库中去了\n");
    }

这里,总结一下:
通过按位与操作来判断某个二进制位是否被标记为1,通过按位或操作将某个二进制位标记为1,然后,就可以把上面的task变量中的内容保存到数据库里,下次该玩家再上线,再把这个内容从数据库中取出,就能判断该玩家的某个任务是否做过,做过的话就可以有一些其他的处理,如不让他再重复做了。

上面这个范例,就是通过位操作的方法,把原本需要10个变量(数字)记录10个任务是否完成缩减成了用一个unsignedint类型变量来记录,一下子就节省了9个变量,这就是位运算在实际工作中的主要用途之一。

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

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

相关文章

多语言多模态(融合图像和文本)大模型-mPLUG-Owl论文解读

近期复现了mPLUG-Owl&#xff0c;效果提升了好几个点&#xff0c;特来精读一番&#xff1a;感谢大佬们的工作&#xff1a; 论文名称&#xff1a;mPLUG-Owl: Modularization Empowers Large Language Models with Multimodality 论文地址&#xff1a;https://arxiv.org/pdf/23…

使用GUI Guider工具开发嵌入式GUI应用(5)-使用timer对象显示动画

使用GUI Guider工具开发嵌入式GUI应用&#xff08;5&#xff09;-使用timer对象显示动画 文章目录 使用GUI Guider工具开发嵌入式GUI应用&#xff08;5&#xff09;-使用timer对象显示动画引言LVGL中的timer对象基于timer对象实现仪表走针小结 引言 设计GUI的显示元素动起来&a…

Websocket原理和实践

一、概述 1.websocket是什么&#xff1f; WebSocket是一种在单个TCP连接上进行全双工通信的协议。WebSocket使得客户端和服务器之间的数据交换变得更加简单&#xff0c;允许服务端主动向客户端推送数据。在WebSocket API中&#xff0c;浏览器和服务器只需要完成一次握手&…

如何快速优化 CnosDB 数据库性能与延迟:使用 Jaeger 分布式追踪系统

在正式的生产环境中&#xff0c;数据库的性能和延迟对于确保系统的稳定和高效运行至关重要。特别是在与 CnosDB 数据库进行交互时&#xff0c;更深入地了解其表现变得尤为重要。这时Jaeger 分布式追踪系统发挥了巨大的作用。在本篇博客中&#xff0c;我们将深入探讨如何通过使用…

ATA-4000系列高压功率放大器——应用场景介绍

ATA-4000系列是一款理想的可放大交、直流信号的高压功率放大器。最大输出310Vp-p(155Vp)电压&#xff0c;452Wp功率&#xff0c;可以驱动高压功率型负载。电压增益&#xff0c;直流偏置数控精细可调&#xff0c;为客户提供了丰富的测试选择。 图&#xff1a;ATA-4000系列高压功…

ndk开发-交叉编译

为什么要使用交叉编译&#xff1a; 在linux系统一般使用c c编译可执行程序或者so库文件。该程序只能在当前linux系统执行&#xff0c;为了将生成文件可以再android平台运行&#xff0c;必须使用交叉编译。ndk中提供了跟多android平台交叉编译链&#xff0c;所以首先下载ndk工具…

FPGA应用学习笔记-----布图布线

分割可以将运行时间惊人地减少到三个小时更小的布局布线操作&#xff0c;主要的结构不影响另一个&#xff01;和增量设计流程一样 关键路径布图&#xff1a; 对于不同的模块有不同的电路和不同的关键路径&#xff0c; 布图没有主要的分割&#xff0c;布图由两个小的区域组成&a…

KDD 2023 获奖论文公布,港中文、港科大等获最佳论文奖

ACM SIGKDD&#xff08;国际数据挖掘与知识发现大会&#xff0c;KDD&#xff09;是数据挖掘领域历史最悠久、规模最大的国际顶级学术会议&#xff0c;也是首个引入大数据、数据科学、预测分析、众包等概念的会议。 今年&#xff0c;第29届 KDD 大会于上周在美国加州长滩圆满结…

C语言入门教程,C语言学习教程(非常详细)第五章 循环结构与选择结构

C语言if else语句详解 前面我们看到的代码都是顺序执行的&#xff0c;也就是先执行第一条语句&#xff0c;然后是第二条、第三条……一直到最后一条语句&#xff0c;这称为顺序结构。 但是对于很多情况&#xff0c;顺序结构的代码是远远不够的&#xff0c;比如一个程序限制了只…

【Javaswing课设源码】学生信息管理 Mysql课程设计 管理员 教师 学生

文章目录 系统介绍 系统介绍 大学时代弄的一个课设&#xff0c;当时百度[学长敲代码]找的代做&#xff0c;代码思路很清晰&#xff0c;完全按照我的功能需求去做的&#xff0c;主要是价格便宜&#xff0c;真的爱了&#xff0c;现在回头学习也是不错的一个项目。大概内容如下 本…

springboot里 用zxing 生成二维码

引入pom <!--二维码依赖--><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.3.3</version></dependency><dependency><groupId>com.google.zxing</groupId>…

【分类讨论】CF1674 E

Problem - E - Codeforces 题意&#xff1a; 思路&#xff1a; 样例&#xff1a; 这种分类讨论的题&#xff0c;主要是去看答案的最终来源是哪几种情况&#xff0c;这几种情况得不重不漏 Code&#xff1a; #include <bits/stdc.h>#define int long longusing i64 lon…

尚硅谷css3笔记

目录 一、新增长度单位 二、新增盒子属性 1.border-box 怪异盒模型 2.resize 调整盒子大小 3.box-shadow 盒子阴影 案例&#xff1a;鼠标悬浮盒子上时&#xff0c;盒子有一个过度的阴影效果 三、新增背景属性 1.background-origin 设置背景图的原点 2.background-clip 设置背…

基于IMX6ULLmini的linux裸机开发系列一:汇编点亮LED

思来想去还是决定记录一下点灯&#xff0c;毕竟万物皆点灯嘛 编程步骤 使能GPIO时钟 设置引脚复用为GPIO 设置引脚属性(上下拉、速率、驱动能力) 控制GPIO引脚输出高低电平 使能GPIO时钟 其实和32差不多 先找到控制LED灯的引脚&#xff0c;也就是原理图 文件名 C:/Us…

自动提示功能消失解决方案

如果绿叶子是不可点击状态&#xff0c;可以点一下列表中的配置文件

43、TCP报文(一)

本节内容开始&#xff0c;我们正式学习TCP协议中具体的一些原理。首先&#xff0c;最重要的内容仍然是这个协议的封装结构和首部格式&#xff0c;因为这里面牵扯到一些环环相扣的知识点&#xff0c;例如ACK、SYN等等&#xff0c;如果这些内容不能很好的理解&#xff0c;那么后续…

A. Copil Copac Draws Trees(Codeforces Round 875 (Div. 1))

Copil Copac is given a list of n − 1 n-1 n−1 edges describing a tree of n n n vertices. He decides to draw it using the following algorithm: Step 0 0 0: Draws the first vertex (vertex 1 1 1). Go to step 1 1 1.Step 1 1 1: For every edge in the inpu…

号外号外,最经典的16S数据库Greengenes2更新啦!!!

没错&#xff0c;这是真的&#xff0c;沉积十年之后&#xff0c;多样性研究中最经典的16S数据库——Greengenes数据库&#xff0c;竟&#xff01;然&#xff01;更&#xff01;新&#xff01;了&#xff01;惊不惊喜&#xff01;意不意外&#xff01; 遥想当年小编还是一个小白…

vue 数字递增(滚动从0到)

使用 html <Incremental :startVal"0" :endVal"1000" :duration"500" />js&#xff1a; import Incremental from /utils/num/numViewjs let lastTime 0 const prefixes webkit moz ms o.split( ) // 各浏览器前缀let requestAnimatio…

基于YOLOv5n/s/m不同参数量级模型开发构建茶叶嫩芽检测识别模型,使用pruning剪枝技术来对模型进行轻量化处理,探索不同剪枝水平下模型性能影响

今天有点时间就想着之前遗留的一个问题正好拿过来做一下看看&#xff0c;主要的目的就是想要对训练好的目标检测模型进行剪枝处理&#xff0c;这里就以茶叶嫩芽检测数据场景为例了&#xff0c;在我前面的博文中已经有过相关的实践介绍了&#xff0c;感兴趣的话可以自行移步阅读…