news 2026/10/5 2:50:20

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

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++位操作实战掩码、提取与组装

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

一、位运算符基础

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

  • 按位与(&):对两个数的每一位执行与操作,仅当两个相应的位都为1时,结果的该位才为1。
  • 按位或(|):对两个数的每一位执行或操作,只要有一个相应的位为1,结果的该位就为1。
  • 按位异或(^):对两个数的每一位执行异或操作,当两个相应的位不同时,结果的该位为1,相同时为0。
  • 按位取反(~):对一个数的每一位执行取反操作,0变为1,1变为0。
  • 左移(<<):将一个数的所有位向左移动指定的位数,右边补0。左移相当于对数字进行2的n次幂的乘法运算(n为移动的位数)。
  • 右移(>>):将一个数的所有位向右移动指定的位数。对于无符号数,高位补0;对于有符号数,处理方式因编译器而异,可能补符号位(算术右移)或补0(逻辑右移)。

示例1:按位与(清零、取指定位)

1

2

3

4

5

6

7

8

9

10

#include <stdio.h>

intmain() {

inttargetValue = 0b11011010;// 二进制表示法

intmask = 0b00110011;

intresult = targetValue & mask;// 应用掩码,保留掩码中为1的位

printf("原始: %08b\n", targetValue);

printf("掩码: %08b\n", mask);

printf("结果: %08b\n", result);

return0;

}

输出:

原始: 11011010
掩码: 00110011
结果: 00011010

示例2:按位或(保留指定位)

1

2

3

4

5

6

7

8

9

10

#include <stdio.h>

intmain() {

inta = 0b00101011;

intb = 0b10010100;

intresult = a | b;// 按位或运算

printf("a: %08b\n", a);

printf("b: %08b\n", b);

printf("结果: %08b\n", result);

return0;

}

输出:

a: 00101011
b: 10010100
结果: 10111111

示例3:按位异或(特定位翻转)

1

2

3

4

5

6

7

8

9

10

#include <stdio.h>

intmain() {

inta = 0b01111010;

intmask = 0b00001111;

intresult = a ^ mask;// 按位异或运算,翻转低4位

printf("原始: %08b\n", a);

printf("掩码: %08b\n", mask);

printf("结果: %08b\n", result);

return0;

}

输出:

原始: 01111010
掩码: 00001111
结果: 01110101

示例4:取反

1

2

3

4

5

6

7

8

9

10

11

#include <stdio.h>

intmain() {

inta = 0b01111010;

intresult = ~a;// 取反运算

printf("原始: %08b\n", a);

printf("结果: %08b\n", result);

return0;

}

输出:

原始: 01111010
结果: 10000101

示例5:左移和右移

1

2

3

4

5

6

7

8

9

10

#include <stdio.h>

intmain() {

inta = 0b00001111;// 15的二进制表示

intleftShiftResult = a << 2;// 左移2位

intrightShiftResult = a >> 2;// 右移2位(逻辑移位)

printf("原始: %08b\n", a);

printf("左移2位: %08b\n", leftShiftResult);// 相当于乘以4,结果为60

printf("右移2位: %08b\n", rightShiftResult);// 相当于除以4,结果为3或-4(取决于符号位和移位方式)

return0;

}

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

原始: 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开始计数)。我们可以使用以下代码:

1

2

3

4

5

6

7

8

#include <stdio.h>

intmain() {

unsignedintnum = 0;// 初始化为0

unsignedintmask = 1 << 5;// 创建一个掩码,第5位为1,其他位为0

num |= mask;// 使用或操作设置第5位

printf("num: %u\n", num);// 输出结果,应该看到第5位被设置为1

return0;

}

示例2:清除特定位

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

1

2

3

4

5

6

7

8

#include <stdio.h>

intmain() {

unsignedintnum = 0x20;// 初始化为0x20(二进制:00100000),第5位被设置

unsignedintmask = ~(1 << 5);// 创建一个掩码,第5位为0,其他位为1

num &= mask;// 使用与操作清除第5位

printf("num: %u\n", num);// 输出结果,应该看到第5位被清除

return0;

}

示例3:检查特定位

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

1

2

3

4

5

6

7

8

9

10

11

12

#include <stdio.h>

intmain() {

unsignedintnum = 0x20;// 初始化为0x20(二进制:00100000),第5位被设置

unsignedintmask = 1 << 5;// 创建一个掩码,第5位为1,其他位为0

intbitIsSet = (num & mask) != 0;// 使用与操作检查第5位是否被设置

if(bitIsSet) {

printf("The 5th bit is set.\n");

}else{

printf("The 5th bit is not set.\n");

}

return0;

}

三、字节提取与组装实战

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

字节提取示例

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

提取低8位(最低字节):

1

2

unsignedcharlowByte = (unsignedchar)(num & 0xFF);

printf("Low byte: 0x%02X\n", lowByte);// 输出:Low byte: 0x78

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

提取第二个字节(从0开始计数):

1

2

unsignedcharsecondByte = (unsignedchar)((num >> 8) & 0xFF);

printf("Second byte: 0x%02X\n", secondByte);// 输出:Second byte: 0x56

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

字节组装示例

现在,假设我们有四个字节byte1 = 0x12,byte2 = 0x34,byte3 = 0x56,byte4 = 0x78,我们想要将它们组合成一个32位无符号整数。

将两个字节组合成一个16位整数:

1

2

unsignedcharsecondByte = (unsignedchar)((num >> 8) & 0xFF);

printf("Second byte: 0x%02X\n", secondByte);// 输出:Second byte: 0x56

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

将四个字节组合成一个32位整数:

1

2

unsignedshortcombined16 = (unsignedshort)((byte1 << 8) | byte2);

printf("Combined 16-bit: 0x%04X\n", combined16);// 输出:Combined 16-bit: 0x1234

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

四、bitset 简介

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

引入头文件和定义bitset

1

2

#include <bitset>

std::bitset<8> myBitset;

常用操作

设置位:

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

1

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

重置位:

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

1

2

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

myBitset.reset();// 重置整个 bitset

翻转位:

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

1

2

myBitset.flip(3);// 翻转第 4 个位

myBitset.flip();// 翻转整个 bitset

检查位:

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

1

boolisBitSet = myBitset.test(3);// 如果第 4 个位是 1,则返回 true,否则返回 false

获取值:

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

1

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

位运算:

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

1

2

std::bitset<8> anotherBitset("10101010");

myBitset &= anotherBitset;// 进行按位与操作

遍历位:

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

1

2

3

4

5

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


版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 2:50:12

GPU热搜词里的2026平台趋势:租用、调度与多架构生态

去年年底我想给自己的工作站换一张大显存的卡&#xff0c;翻了一晚上行情&#xff0c;最后把预算从“买卡”改成了“租卡”。这个决定本身没什么稀奇&#xff0c;但真正让我有感触的是&#xff0c;当我把GPU相关的热搜词拉出来看了一遍之后&#xff0c;发现整个行业的需求结构已…

作者头像 李华
网站建设 2026/10/5 2:50:11

Redis从入门到实战:数据类型、持久化、分布式锁与高可用

第一次接触 Redis 的时候&#xff0c;我以为它只是一个长得像字典的缓存库&#xff0c;把数据往内存里一扔&#xff0c;读得快、写得快&#xff0c;完事。后来真正做项目才发现&#xff0c;这个念头差点让我在缓存穿透、数据一致性和分布式锁上栽大跟头。Redis 之所以被叫做“缓…

作者头像 李华
网站建设 2026/10/5 2:49:31

学Git先掌握这15个核心命令:从安装配置到分支合并一次讲透

有人问我&#xff0c;学 Git 到底先学什么&#xff1f;我的答案一直没变过&#xff1a;先别急着背命令&#xff0c;先把日常开发里最高频的那十几个命令用熟。Git 的命令有上百个&#xff0c;但说实话&#xff0c;你每天真正敲来敲去的&#xff0c;翻来覆去就是那十几个。把这十…

作者头像 李华
网站建设 2026/10/5 2:49:31

DeepSeek R1本地部署与知识库搭建:从Ollama到Dify实战指南

简介&#xff1a;PDF教程围绕DeepSeek R1的本地部署展开&#xff0c;面向想摆脱云端依赖、在个人电脑上运行大语言模型的开发者与普通用户。内容从安装Ollama入手&#xff0c;涵盖模型版本选择、命令行验证&#xff0c;再到Cherry-Studio界面化配置与密钥创建&#xff0c;最后讲…

作者头像 李华
网站建设 2026/10/5 2:49:02

SpringBoot实战:NBA数据分析系统开发全解析

带一份 SpringBoot 做数据分析系统&#xff0c;我当初选这个题&#xff0c;就是看中它“能跑通、能讲透、能扩展”。NBA 这个题材在课程设计和毕业设计里都属于讨喜的类型——导师一听就知道你要做什么&#xff0c;不用费劲解释业务背景&#xff1b;评审老师看演示的时候&#…

作者头像 李华
网站建设 2026/10/5 2:49:02

Java程序员转型大模型开发:向量数据库与RAG全攻略

Java程序员这个群体&#xff0c;过去十年被问最多的问题就是“你们到底是不是只会增删改查”&#xff0c;这几年风向又变了&#xff0c;变成“你会不会大模型开发”。我见过太多同事&#xff0c;一边刷着Spring Boot面试题&#xff0c;一边焦虑AI时代自己会不会被优化。其实Jav…

作者头像 李华