C++位操作实战:掩码、提取与组装

在C++编程中,位操作是一项基础且强大的技术,它允许程序员在二进制级别上直接操作数据。这种能力对于性能优化、内存节省以及底层硬件控制至关重要。本文将深入探讨C++中的掩码操作、字节提取与组装,并通过实例展示这些技术的实际应用。


一、位运算符基础

C++中的基本位运算符:

  • 按位与(&):对两个数的每一位执行与操作,仅当两个相应的位都为1时,结果的该位才为1。
  • 按位或(|):对两个数的每一位执行或操作,只要有一个相应的位为1,结果的该位就为1。
  • 按位异或(^):对两个数的每一位执行异或操作,当两个相应的位不同时,结果的该位为1,相同时为0。
  • 按位取反(~):对一个数的每一位执行取反操作,0变为1,1变为0。
  • 左移(<<):将一个数的所有位向左移动指定的位数,右边补0。左移相当于对数字进行2的n次幂的乘法运算(n为移动的位数)。
  • 右移(>>):将一个数的所有位向右移动指定的位数。对于无符号数,高位补0;对于有符号数,处理方式因编译器而异,可能补符号位(算术右移)或补0(逻辑右移)。
示例1:按位与(清零、取指定位)
#include <stdio.h>int main() {int targetValue = 0b11011010; // 二进制表示法int mask = 0b00110011;int result = targetValue & mask; // 应用掩码,保留掩码中为1的位printf("原始: %08b\n", targetValue);printf("掩码: %08b\n", mask);printf("结果: %08b\n", result);return 0;
}

输出:

原始: 11011010
掩码: 00110011
结果: 00011010
示例2:按位或(保留指定位)
#include <stdio.h>int main() {int a = 0b00101011;int b = 0b10010100;int result = a | b; // 按位或运算printf("a: %08b\n", a);printf("b: %08b\n", b);printf("结果: %08b\n", result);return 0;
}

输出:

a: 00101011
b: 10010100
结果: 10111111
示例3:按位异或(特定位翻转)
#include <stdio.h>int main() {int a = 0b01111010;int mask = 0b00001111;int result = a ^ mask; // 按位异或运算,翻转低4位printf("原始: %08b\n", a);printf("掩码: %08b\n", mask);printf("结果: %08b\n", result);return 0;
}

输出:

原始: 01111010
掩码: 00001111
结果: 01110101
示例4:取反
#include <stdio.h>int main() {int a = 0b01111010;int result = ~a; // 取反运算printf("原始: %08b\n", a);printf("结果: %08b\n", result);return 0;
}

输出:

原始: 01111010
结果: 10000101
示例5:左移和右移
#include <stdio.h>int main() {int a = 0b00001111; // 15的二进制表示int leftShiftResult = a << 2; // 左移2位int rightShiftResult = a >> 2; // 右移2位(逻辑移位)printf("原始: %08b\n", a);printf("左移2位: %08b\n", leftShiftResult); // 相当于乘以4,结果为60printf("右移2位: %08b\n", rightShiftResult); // 相当于除以4,结果为3或-4(取决于符号位和移位方式)return 0;
}

输出(假设为逻辑移位):

原始: 00001111
左移2: 00111100
右移2: 00000011

二、掩码操作实战

掩码是一个二进制数,用于屏蔽不需要的位,只保留目标位。通过与操作(&),可以保留掩码中为1的位,其他位都被清零。在C/C++中使用掩码操作来设置、清除和检查整数的特定位。这些技术在性能优化、内存节省以及底层硬件控制中非常有用。

  • 设置特定位:通过掩码与或操作,可以设置整数的特定位。例如,要设置32位整数的第5位(从0开始计数),可以使用num | (1 << 5)
  • 清除特定位:通过掩码与取反操作,可以清除整数的特定位。例如,要清除32位整数的第5位,可以使用num & ~(1 << 5)
  • 检查特定位:通过与操作,可以检查整数的特定位是否被设置。例如,要检查32位整数的第5位是否被设置,可以使用(num & (1 << 5)) != 0
示例1:设置特定位

假设我们有一个32位整数num,我们想要设置其中的第5位(从0开始计数)。我们可以使用以下代码:

#include <stdio.h>  int main() {  unsigned int num = 0; // 初始化为0  unsigned int mask = 1 << 5; // 创建一个掩码,第5位为1,其他位为0  num |= mask; // 使用或操作设置第5位  printf("num: %u\n", num); // 输出结果,应该看到第5位被设置为1  return 0;  
}
示例2:清除特定位

现在,假设我们想要清除num的第5位。我们可以使用以下代码:

#include <stdio.h>  int main() {  unsigned int num = 0x20; // 初始化为0x20(二进制:00100000),第5位被设置  unsigned int mask = ~(1 << 5); // 创建一个掩码,第5位为0,其他位为1  num &= mask; // 使用与操作清除第5位  printf("num: %u\n", num); // 输出结果,应该看到第5位被清除  return 0;  
}
示例3:检查特定位

最后,假设我们想要检查num的第5位是否被设置。我们可以使用以下代码:

#include <stdio.h>  int main() {  unsigned int num = 0x20; // 初始化为0x20(二进制:00100000),第5位被设置  unsigned int mask = 1 << 5; // 创建一个掩码,第5位为1,其他位为0  int bitIsSet = (num & mask) != 0; // 使用与操作检查第5位是否被设置  if (bitIsSet) {  printf("The 5th bit is set.\n");  } else {  printf("The 5th bit is not set.\n");  }  return 0;  
}

三、字节提取与组装实战

  • 字节提取:通过右移和掩码操作,可以提取整数的特定字节。
  • 字节组装:通过左移和按位或操作,可以将多个字节组合成一个整数。
字节提取示例

假设我们有一个32位无符号整数num,其值为0x12345678(十六进制表示,二进制为00010010 00110100 01010110 01111000)。

  1. 提取低8位(最低字节)
unsigned char lowByte = (unsigned char)(num & 0xFF);printf("Low byte: 0x%02X\n", lowByte); // 输出:Low byte: 0x78

这里,0xFF是一个掩码,其二进制表示为11111111。通过与操作&,我们保留了num的低8位,并将其他位清零。然后,我们将结果强制转换为unsigned char类型,以确保它是一个字节大小。

  1. 提取第二个字节(从0开始计数)
   unsigned char secondByte = (unsigned char)((num >> 8) & 0xFF);printf("Second byte: 0x%02X\n", secondByte); // 输出:Second byte: 0x56

首先,我们通过右移操作>> 8num的所有位向右移动8位,这样原来的第二个字节就变成了新的低字节。然后,我们再次使用0xFF掩码和与操作来提取这个新的低字节。

字节组装示例

现在,假设我们有四个字节byte1 = 0x12byte2 = 0x34byte3 = 0x56byte4 = 0x78,我们想要将它们组合成一个32位无符号整数。

  1. 将两个字节组合成一个16位整数
   unsigned short combined16 = (unsigned short)((byte1 << 8) | byte2);printf("Combined 16-bit: 0x%04X\n", combined16); // 输出:Combined 16-bit: 0x1234

这里,我们首先通过左移操作<< 8byte1的所有位向左移动8位,为byte2腾出空间。然后,我们使用按位或操作|byte1(左移后的)和byte2组合起来。

  1. 将四个字节组合成一个32位整数
   unsigned int combined32 = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;printf("Combined 32-bit: 0x%08X\n", combined32); // 输出:Combined 32-bit: 0x12345678

类似地,我们分别将byte1byte2byte3向左移动24位、16位和8位,然后将它们与byte4通过按位或操作组合起来。

四、bitset 简介

bitset 是 C++ 标准库中一个非常有用的类模板,它可以帮助我们高效地处理二进制数据。通过使用 bitset,我们可以方便地进行位设置、重置、翻转、检查、获取值以及位运算等操作。此外,bitset 还提供了遍历设置为 1 的位的功能,使得处理二进制数据变得更加灵活和方便。

引入头文件和定义 bitset
#include <bitset>
std::bitset<8> myBitset;
常用操作
  1. 设置位

    使用 set() 函数可以将某个位设置为 1。例如:

    myBitset.set(3); // 将第 4 个位(索引从 0 开始)设置为 1
    
  2. 重置位

    使用 reset() 函数可以将某个位设置为 0。如果调用时不带参数,则会重置整个 bitset。例如:

    myBitset.reset(3); // 将第 4 个位重置为 0
    myBitset.reset();  // 重置整个 bitset
    
  3. 翻转位

    使用 flip() 函数可以翻转某个位或者整个 bitset 的值。如果调用时不带参数,则会翻转整个 bitset。例如:

    myBitset.flip(3); // 翻转第 4 个位
    myBitset.flip();  // 翻转整个 bitset
    
  4. 检查位

    使用 test() 函数可以检查某个位是否为 1。例如:

    bool isBitSet = myBitset.test(3); // 如果第 4 个位是 1,则返回 true,否则返回 false
    
  5. 获取值

    使用 to_string() 函数可以获取 bitset 的字符串表示。例如:

    std::string bitsetString = myBitset.to_string(); // 返回一个表示 bitset 值的字符串
    
  6. 位运算

    bitset 还支持一些位运算操作,如按位与、按位或、按位异或等。例如:

    std::bitset<8> anotherBitset("10101010");
    myBitset &= anotherBitset; // 进行按位与操作
    
  7. 遍历位

    使用 find_first()find_next() 函数可以遍历设置为 1 的位。例如:

    std::size_t pos = myBitset.find_first(); // 找到第一个设置为 1 的位的索引
    while (pos != std::bitset<8>::npos) {// 处理设置为 1 的位pos = myBitset.find_next(pos); // 找到下一个设置为 1 的位的索引
    }
    

五、其他位操作技术

  • 位旋转:涉及将整数的位向左或向右循环移动。可以通过组合左移、右移和按位或操作来实现。
  • 位计数:计算一个整数中设置为1的位的数量。可以使用逐位检查或使用更高效的算法(如Brian Kernighan算法)。
  • 位查找:找到整数中第一个或最后一个设置为1的位的位置。可以使用逐位检查或使用内置函数(如__builtin_ctz__builtin_clz,取决于编译器)。
  • 位字段(Bit-fields):位字段是C和C++中一种特殊的数据结构,允许在结构体中定义位级别的成员。虽然位字段在节省内存空间方面非常有用,但跨平台兼容性可能存在问题,因为不同编译器对位字段的布局和填充有不同的处理方式。因此,在使用位字段时需要谨慎,并确保在目标平台上进行充分的测试。

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

react18中react-thunk实现公共数据仓库的异步操作

redux及react-redux都只能实现数据的同步修改更新&#xff0c;有点类似于vue中的mutation&#xff0c;只能做同步操作&#xff0c;异步的话不用actions来实现。由于在项目始终不可避免要实现的异步数据的更新&#xff0c;这明显不够用了。是时候引入我们的异步中间件redux-thun…

开源一款前后端分离的企业级网站内容管理系统,支持站群管理、多平台静态化,多语言、全文检索的源码

大家好&#xff0c;我是一颗甜苞谷&#xff0c;今天分享一款前后端分离的企业级网站内容管理系统&#xff0c;支持站群管理、多平台静态化&#xff0c;多语言、全文检索的源码。 前言 在当今的数字化时代&#xff0c;企业网站和个人博客已成为信息传播和品牌建设的重要渠道。…

Docker-常用命令大全(附命令详解)

文章目录 Docker 基础命令查看docker 运行状态关闭docker启动docker重启dockerdocker设置随服务启动而自启动查看docker 版本号信息docker 帮助命令 docker 镜像命令查看自己服务器中docker 镜像列表搜索镜像拉取镜像运行镜像保存镜像删除镜像加载镜像镜像标签 Docker 容器命令…

【ComfyUI】手动安装部署ComfyUI的运行环境

如果不喜欢已有的一键启动包&#xff0c;我们可以手动的安装和部署ComfyUI的运行环境&#xff0c;相比一键安装包&#xff0c;自己部署ComfyUI 环境具有相当大的灵活性&#xff0c;其实部署ComfyUI 环境非常简单&#xff0c;不像网上说的那么复杂。下面我们就按照顺序给大家分享…

Golang | Leetcode Golang题解之第520题检测大写字母

题目&#xff1a; 题解&#xff1a; func detectCapitalUse(word string) bool {// 若第 1 个字母为小写&#xff0c;则需额外判断第 2 个字母是否为小写if len(word) > 2 && unicode.IsLower(rune(word[0])) && unicode.IsUpper(rune(word[1])) {return f…

【Cri-Dockerd】安装cri-dockerd

cri-dockerd的作用&#xff1a; 在k8s1.24之前。k8s会通过dockershim来调用docker进行容器运行时containerd&#xff0c;并且会自动安装dockershim&#xff0c;但是从1.24版本之前k8s为了降低容器运行时的调用的复杂度和效率&#xff0c;直接调用containerd了&#xff0c;并且…

git下载和配置

git是什么&#xff1f; Git是一种分布式版本控制系统&#xff0c;用于跟踪文件的变化&#xff0c;尤其是源代码。它允许多个开发者在同一项目上进行协作&#xff0c;同时保持代码的历史记录。Git的主要特点包括&#xff1a; 分布式&#xff1a;每个开发者都有项目的完整副本&a…

GPT避坑指南:如何辨别逆向、AZ、OpenAI官转

市面上有些说自己是官转&#xff0c;一刀只需要1块甚至几毛钱&#xff0c;并声称官方倍率的&#xff0c;很大可能就是使用的是 逆向或Azure。 如何鉴别逆向 逆向的种类很多&#xff0c;主要分为3类 逆向不知名A| 镜像站或偷的 key。成本约等于0&#xff0c;调用聊天数据可能在…

postgresql增量备份系列一

简介 在一些大容量得数据库应用中&#xff0c;采用全量备份得方式&#xff0c;会带来大量时间浪费和开销&#xff0c;此时定期的增量备份可以使得数据存储周期变长。本文讲解几个增量备份工具 pg_basebackup pg_receivewal&#xff08;异地归档模式&#xff09; 使用pg_bas…

arcgis pro 3.3.1安装教程

一、获取方式&#xff1a; http://dt4.8tupian.net/2/29913a61b1500.pg3二、软件目录&#xff1a; 三、安装步骤&#xff1a; &#xff08;1&#xff09;安装软件运行环境windowsdesktop-runtime 8.0.4; &#xff08;2&#xff09;选中安装文件arcgispro_33zh_cn_190127.exe&…

LabVIEW汽车状态监测系统

LabVIEW汽车状态监测系统通过模拟车辆运行状态&#xff0c;有效地辅助工程师进行故障预测和维护计划优化&#xff0c;从而提高汽车的可靠性和安全性。 项目背景&#xff1a; 现代汽车工业面临着日益增长的安全要求和客户对于车辆性能的高期望。汽车状态监测系统旨在实时监控汽…

GiantPandaCVARM Neon Intrinsics 学习指北:从入门、进阶到学个通透

【GiantPandaCV导语】Neon是手机普遍支持的计算加速指令集&#xff0c;是AI落地的工程利器。Neon Intrinsics 的出现&#xff0c;缓解了汇编语言难学难写的难题&#xff0c;值得工程师们开发利用。 前言 Neon是ARM平台的向量化计算指令集&#xff0c;通过一条指令完成多个数据…

python爬虫抓取豆瓣数据教程

环境准备 在开始之前&#xff0c;你需要确保你的Python环境已经安装了以下库&#xff1a; requests&#xff1a;用于发送HTTP请求。BeautifulSoup&#xff1a;用于解析HTML文档。 如果你还没有安装这些库&#xff0c;可以通过以下命令安装&#xff1a; pip install requests…

SD-WAN分布式组网:构建高效、灵活的企业网络架构

随着企业数字化转型的深入&#xff0c;分布式组网逐渐成为企业网络架构中的核心需求。无论是跨区域的分支机构互联&#xff0c;还是企业与云服务的连接&#xff0c;如何在不同区域实现高效、低延迟的网络传输&#xff0c;已成为业务成功的关键。SD-WAN&#xff08;软件定义广域…

使用Python和OpenCV实现火焰检测

使用Python和OpenCV实现火焰检测 项目解释&#xff1a; 此 Python 代码是使用 OpenCV、线程、声音和电子邮件功能的火灾探测系统的简单示例。 以下是它的功能的简单描述&#xff1a; 导入库&#xff1a;代码首先导入必要的库&#xff1a; cv2&#xff1a;用于图像和视频处理…

一篇文章入门傅里叶变换

文章目录 傅里叶变换欧拉公式傅里叶变换绕圈记录法质心记录法傅里叶变换公式第一步&#xff1a;旋转的表示第二步&#xff1a;缠绕的表示第三步&#xff1a;质心的表示最终步&#xff1a;整理积分限和系数 参考文献 傅里叶变换 在学习傅里叶变换之前&#xff0c;我们先来了解一…

基于uniapp微信小程序的校园二手书交易系统

作者&#xff1a;计算机学姐 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等&#xff0c;“文末源码”。 专栏推荐&#xff1a;前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码、微信小程序源码 精品专栏&#xff1a;…

Spring 框架环境搭建

一、环境要求 JDK版本&#xff1a; JDK1.7及以上版本 Spring版本&#xff1a; Spring5.x版本 二、新建Maven项目 1. 创建 Maven 的普通 Java 项⽬ 2.设置项目坐标 3.设置项目的Maven环境 4.设置项目的名称和存放的工作空间 三、调整项目环境 1.修改JDK版本 properties&g…

前端 eslint 配置,以及在git提交之前自动format

目录 1、配置eslint步骤 1、eslint安装配置步骤 2、配置scripts步骤 3、测试eslint 2、配置git-hook1、安装环境2、最终效果 众所周知&#xff0c;前端项目可以在报很多error的情况下运行。但是良好的代码规范仍然有利于项目的开发维护&#xff0c;这里提供我的规范&#xff0c…

SpringBoot篇(运维实用篇 - 临时属性)

目录 一、临时属性设置 1. 简介 2. 属性加载优先级 那是否还有其他的配置方式呢&#xff1f; 3. 知识小结 二、开发环境中使用临时属性 1. 如何操作 2. 知识小结 3. 思考 三、配置文件分类 1. 简介 2. 4个级别 3. 为什么设计多种配置文件&#xff1f; 一个典型的应…