当前位置: 首页 > news >正文

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++中一种特殊的数据结构,允许在结构体中定义位级别的成员。虽然位字段在节省内存空间方面非常有用,但跨平台兼容性可能存在问题,因为不同编译器对位字段的布局和填充有不同的处理方式。因此,在使用位字段时需要谨慎,并确保在目标平台上进行充分的测试。

相关文章:

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

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

PVE虚拟机强制重启

在Proxmox VE (PVE) 中&#xff0c;强制重启虚拟机的方法有几种&#xff0c;取决于具体场景和虚拟机的状态。以下是常用的两种方法&#xff1a; 1. 使用PVE Web UI强制重启虚拟机 如果虚拟机无法正常关闭或重启&#xff0c;可以使用PVE Web界面中的强制关机/重启选项&#xf…...

Adobe Acrobat DC 打印PDF文件,没有打印出注释的解决方法

adobe acrobat在打印的时候&#xff0c;打印不出来注释内容&#xff08;之前一直可以&#xff0c;突然就不行&#xff09;&#xff0c;升级版本、嵌入字体等等都试过&#xff0c;也在Google找了半天和问了GPT也么找着办法。 无奈之下&#xff0c;自己通过印前检查&#xff0c;…...

主机名学习

1.主机名 定义&#xff1a;主机名是一个人类可读的标识符&#xff0c;通常由字母、数字和连接符组成&#xff0c;用于标识网络中的设备。主机名可以是局部的&#xff08;例如局域网中的设备名&#xff09;或者全局的&#xff08;通过 DNS 解析成 IP 地址&#xff09;。 解析&…...

SpringBoot循环依赖

在Spring Boot&#xff08;以及Spring框架&#xff09;中&#xff0c;循环依赖是指两个或多个Bean互相依赖&#xff0c;导致Spring在创建这些Bean时无法正常进行依赖注入。例如&#xff0c;假设有两个类A和B&#xff0c;A依赖于B&#xff0c;而B又依赖于A。在这种情况下&#x…...

一道面试题:为什么要使用Docker?

先来笼统地看一下 1、环境一致性 众所周知&#xff0c;开发过程中一个常见的问题是环境一致性问题&#xff0c;由于开发环境&#xff0c;测试环境&#xff0c;生产环境不一致&#xff0c;导致有些bug并未在开发过程中被发现&#xff0c;而Docker的镜像提供了除内核外完整的运…...

类的创建、构造器、实例属性、实例方法

Creating Classes # Class: blueprint for creating new Objects # Object: instance of a class # Class: Human # Objects: John, Mary, Jack# 类名定义每个单词的首字母大写 class Point:# 每个方法至少有一个参数def draw(self):print("draw")# 创建Point对象 p…...

js读取.txt文件内容

方法一&#xff1a;FileReader() <input type"file" id"fileInput" /><script>const fileInput document.getElementById(fileInput)fileInput.addEventListener(change, function (e) {const file e.target.files[0]const reader new Fil…...

【AscendC算子开发】笔记2 算子高级开发和调试调优

算子调试 Tensor也可以通过特定的printf方法来打印&#xff0c;见上图。 gdb调试见上图。 为什么gdb调试无法成功&#xff0c;因为run.sh里面有两行export&#xff0c;如果直接通过.XX运行的话需要配置一下。 npu域也支持调试&#xff0c;可以使用上述的方法。 内存检测工…...

vscode连接keil-5 开发STM32 程序

Vs Code连接 keil-5 &#xff0c;开发STM32 程序 为了不使用难用的 keil-5 软件 继续使用友好的 vscode开发 步骤&#xff1a; 1 安装Keil Assitant 扩展 2 将你的 Keil-5 这个程序的路径找到复制到这里 3 下载 Mingw 下载点此处 4 下载后只解压 就行了 不安装 下载完 放在…...

数据结构深度优先搜索遍历连通图+非连通图(C语言代码+遍历+终端输入内容)

首先数据结构(C语言版第二版)的关于深度优先搜索遍历连通图的图G4如下: 使用邻接表去创建上面这个无向图&#xff0c;然后再使用书本DFS函数以及DFSTraverse函数实现深度优先搜索遍历 #define _CRT_SECURE_NO_WARNINGS 1 #include<stdio.h> #include<stdlib.h> #…...

信息安全工程师(55)网络安全漏洞概述

一、定义 网络安全漏洞&#xff0c;又称为脆弱性&#xff0c;是网络安全信息系统中与安全策略相冲突的缺陷&#xff0c;这种缺陷也称为安全隐患。漏洞可能导致机密性受损、完整性破坏、可用性降低、抗抵赖性缺失、可控性下降、真实性不保等问题。 二、分类 网络安全漏洞可以根据…...

member access within null pointer of type ‘ListNode‘

文章目录 前言一、空指针解引用二、访问已释放的内存三、 结构体定义问题四、错误的链表操作五、代码上下文六、示例代码七、调试建议 前言 p -> next p1; p1 p1 -> next; p p->next;runtime error: member access within null pointer of type ListNode如果出现…...

UE5蓝图中整理节点的方法

UE5蓝图中整理节点的方法 第一种&#xff1a;子图 右键选中的节点&#xff0c;出现一个面板&#xff0c;点击 Collapse Nodes 既可折叠选中的所有节点 注意&#xff1a;子图不可以被复制使用。 双击子图可以查看节点&#xff0c;若不想折叠选中的节点为子图&#xff0c;右键点…...

01,http 协议

1 &#xff0c;http 协议 &#xff1a;介绍 1 &#xff0c;http &#xff1a;是什么 Hyper Text Transfer Protocol &#xff1a;超文本传输协议 2 &#xff0c;传输内容 &#xff1a;文本 1 &#xff0c;内容 &#xff1a;      纯文本    2 &#xff0c;特殊 &#xf…...

在 typescript 中,如何封装一个 class 类来接收接口的响应数据

在 TypeScript 中&#xff0c;封装一个类来接收接口的响应数据是一个常见的需求&#xff0c;特别是在处理后端 API 响应时。这通常涉及到定义与后端 API 响应结构相匹配的接口&#xff08;或类型&#xff09;&#xff0c;并在类中创建方法来处理这些数据。以下是一个简单的示例…...

力扣周赛第420场 中等 3325.字符至少出现k次的子字符串 I

文章目录 题目介绍题解 题目介绍 题解 滑动窗口思想&#xff1a;参考 3.无重复字符的最长子串 链接 代码如下&#xff1a; class Solution {public int numberOfSubstrings(String s, int k) {int n s.length(), res 0;for(int left 0; left < n; left){// 记录窗口内…...

【Spring框架】Spring核心思想IoC以及依赖注入DI详解

目录 Spring框架前言 服务端三层开发表现层业务层持久层 Spring框架的概述Spring框架的优点Spring核心——IoC什么是IoC&#xff1f;O.o什么是耦合度&#xff1f; 创建第一个IoC程序导入必要依赖编写接口和实现类编写Spring核心配置文件测试类进行测试 Spring配置文件Bean对象的…...

Java项目-基于springboot框架的智慧外贸系统项目实战(附源码+文档)

作者&#xff1a;计算机学长阿伟 开发技术&#xff1a;SpringBoot、SSM、Vue、MySQL、ElementUI等&#xff0c;“文末源码”。 开发运行环境 开发语言&#xff1a;Java数据库&#xff1a;MySQL技术&#xff1a;SpringBoot、Vue、Mybaits Plus、ELementUI工具&#xff1a;IDEA/…...

Python程序控制结构 if语句详解

前面我们已经详细介绍了Python编程基础入门&#xff1a;从风格到数据类型再到表达式 在编程中&#xff0c;控制结构决定了代码的执行顺序。Python提供了丰富的控制结构&#xff0c;可以帮助程序根据不同条件做出不同的决策和操作。本文将深入介绍Python中常见的控制结构——包…...

【Axure高保真原型】引导弹窗

今天和大家中分享引导弹窗的原型模板&#xff0c;载入页面后&#xff0c;会显示引导弹窗&#xff0c;适用于引导用户使用页面&#xff0c;点击完成后&#xff0c;会显示下一个引导弹窗&#xff0c;直至最后一个引导弹窗完成后进入首页。具体效果可以点击下方视频观看或打开下方…...

Docker 离线安装指南

参考文章 1、确认操作系统类型及内核版本 Docker依赖于Linux内核的一些特性&#xff0c;不同版本的Docker对内核版本有不同要求。例如&#xff0c;Docker 17.06及之后的版本通常需要Linux内核3.10及以上版本&#xff0c;Docker17.09及更高版本对应Linux内核4.9.x及更高版本。…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

IT供电系统绝缘监测及故障定位解决方案

随着新能源的快速发展&#xff0c;光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域&#xff0c;IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选&#xff0c;但在长期运行中&#xff0c;例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...

聊一聊接口测试的意义有哪些?

目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开&#xff0c;首…...

Maven 概述、安装、配置、仓库、私服详解

目录 1、Maven 概述 1.1 Maven 的定义 1.2 Maven 解决的问题 1.3 Maven 的核心特性与优势 2、Maven 安装 2.1 下载 Maven 2.2 安装配置 Maven 2.3 测试安装 2.4 修改 Maven 本地仓库的默认路径 3、Maven 配置 3.1 配置本地仓库 3.2 配置 JDK 3.3 IDEA 配置本地 Ma…...

安宝特案例丨Vuzix AR智能眼镜集成专业软件,助力卢森堡医院药房转型,赢得辉瑞创新奖

在Vuzix M400 AR智能眼镜的助力下&#xff0c;卢森堡罗伯特舒曼医院&#xff08;the Robert Schuman Hospitals, HRS&#xff09;凭借在无菌制剂生产流程中引入增强现实技术&#xff08;AR&#xff09;创新项目&#xff0c;荣获了2024年6月7日由卢森堡医院药剂师协会&#xff0…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲

文章目录 前言第一部分&#xff1a;体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分&#xff1a;体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...

Web后端基础(基础知识)

BS架构&#xff1a;Browser/Server&#xff0c;浏览器/服务器架构模式。客户端只需要浏览器&#xff0c;应用程序的逻辑和数据都存储在服务端。 优点&#xff1a;维护方便缺点&#xff1a;体验一般 CS架构&#xff1a;Client/Server&#xff0c;客户端/服务器架构模式。需要单独…...

解析“道作为序位生成器”的核心原理

解析“道作为序位生成器”的核心原理 以下完整展开道函数的零点调控机制&#xff0c;重点解析"道作为序位生成器"的核心原理与实现框架&#xff1a; 一、道函数的零点调控机制 1. 道作为序位生成器 道在认知坐标系$(x_{\text{物}}, y_{\text{意}}, z_{\text{文}}…...