C生万物 | 十分钟带你学会位段相关知识
结构体相关知识可以先看看这篇文章 —— 链接
一、什么是位段
位段的声明和结构是类似的,有两个不同:
- 位段的成员必须是
int
、unsigned int
或signed int
- 位段的成员名后边有一个冒号和一个数字
- 在下面,我分别写了一个结构体和一个位段,注意看位段的写法和结构体有什么不同
//结构体
struct A {int a;int b;int c;int d;
};
//位段
struct B{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};
- 然后我们
sizeof
去计算一下这个结构体的大小
printf("结构体大小:%d\n", sizeof(struct A));
printf("位段大小:%d\n", sizeof(struct B));
可以看到,结构体的大小是16,位段是8,二者为何会存在区别呢?原因在于这个: 2
吗?
- 那根据位段后面的这些数字,我们可以初步去断定可能大小是这些数组的总和,再转换为字节的。计算一下可以知道为
47b
,在内存中1B = 8b
,要存下这个47个比特位的话应该6个字节就够了,但是结果为什么是8呢?我们不得而知😐
学习了位段的相关知识后你就知道了
二、位段的内存分配
首先来科普一下位段的相关知识📖
- 位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型
- 位段的空间上是按照需要以4个字节
[int]
或者1个字节[char]
的方式来开辟的。 - 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段
- 那从上面我们就可以提取出一些信息,知道了对于整型而言会开辟出4个字节的数据给到位段作为存放,那接下去呢我们就来分析一下这个位段
- 仔细观察可以得知每个成员都是整型,那首先开辟出32个比特位
_a
占了2个比特位,还剩下【30b】_b
占了5个比特位,还剩下【25b】_c
占了10个比特位,还剩下【15b】_d
占了30个比特位,但是剩下的【15b】不够用了,此时编译器会继续开辟出4B,也就是32b的空间来存放
- 所以最后的结果就是
4 + 4 = 8B
struct B{//4Byte - 32bitint _a : 2; //30int _b : 5; //25int _c : 10; //15//4Byte - 32bitint _d : 30;//4 + 4 = 8
};
看了我上面的这样计算,你一定会有这些疑问
💬 第一次是32b用剩后的【15b】去哪儿了呢?
💬 _d
使用的是【15b】+ 后面开辟出来的32b,还是只用到后面的32b呢?
💬 难道所有平台都是这样吗?有没有不一样的计算方法?
- 上面是很多同学在课后提出来的疑问,关于这些,你在看完了我下面的分析后就会明白了👇
内存图分析位段分布
接下去我就通过对位段进行分析,然后观察内存分布来揭晓上面究竟是如何计算的。
为了方便期间,这里换一组位段,但是换汤不换药
struct S
{char a : 3;char b : 4;char c : 5;char d : 4;
};int main(void)
{struct S s = { 0 };s.a = 10;s.b = 12;s.c = 3;s.d = 4;return 0;
}
- 首先来看一下存放这个位段需要的字节数。可以看到这个位段中的每个成员都是
char
类型的,所以编译器会首先为其分配一个字节的空间,然后随着变量的存入,最终是需要三个字节
然后我们来逐一分析一下💻
- 刚才说了,这个位段在内存中需要开辟三个字节,这些变量要怎么存呢,首先看到变量a占了3个比特位,那是从左边的三位开始放还是右边的三位呢?总不可以从中间开始放吧!
- 那我们假设一下,从右边往左边放,那么
a
放完后就是b
,占4个比特位,但是放c
的时候就放不下了,所以需要在开辟1个字节的空间,此时d
再来放的话也放不下了,所以也要再开辟1个字节 ,最后也就需要3个字节的空间
【详细分析如下】:
- 接下去我们就根据main函数中对位段各变量的初始化,来看看位段在内存中的分布情况:
a
的初始值为10,不过这是十进制,转换为二进制形式的话就是[1010]
,转看位段这里a变量的是占了3位,所以会截断成010
,将它放到第一个字节处的右边3个比特位处 - 接下去是
b
,初始值为12
,转换为二进制形式的话就是[1100]
,而b在内存中也刚好是占4个比特位的大小,刚好第一个字节处还可以放得过,所以继续顺位放置 - 然后是
c
,初始值为3
,转换为二进制形式的话就是11
,但是c在内存中也占5个比特位的大小,所以要在前面做一个扩充便为[00011]
,但是第一个字节放不下了,上面放了【3】+【4】=【7】,只剩下1个比特位,那我们考虑再开一个字节的空间,为了保持连续性就直接把这个5个比特位的数据放到第二个字节的右边 - 最后的是
d
,初始值为4
,转换为二进制形式的话就是100
,不过d在内存中也占4个比特位的大小,所以要在前面补上一个0,即为[0100]
,但是第二个字节也放不过了,只剩三个比特位了,所以我们考虑再开一个字节的空间,然后放这个d
上面只是我假设的编译器执行思维,不过真正是怎样的,我们还是要求证一下
- 那要怎么求证呢?这个很简单,既然这些变量都是存放在位段中,那我们刚才都算出所存放的二进制形式了。对于内存中的地址一般我们看到都是十六进制,所以可以考虑把这些二进制4个为一组转换为十六进制看看
01100010
即为——>0x62
00000011
即为——>0x03
00000100
即为——>0x04
- 而在内存中左边是低地址,右边是高地址,所以我们看到的应该是
62 03 04 cc
。来通过【内存】观察一下吧
可以看到,确实和我们分析得是一模一样✌
看完了上面这个,相信你对一开始的那个位段如何去进行求解的整个流程应该是非常清楚了,留给读者自己的分析观察🔍
三、位段的跨平台问题
接下去我们再来讲讲有关位段的跨平台的问题
- int 位段被当成有符号数还是无符号数是不确定的
- 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32)
- 假设我们将位段中一个变量所占大小设置为30,即占30个比特位,那么它在32为机器上是没问题的,但是放到早期的16位机器上去的话,可能连编译都编不过,因为根本存放不下
- 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义
- 刚才我分析的时候假设的是从右往左进行分配,但是呢这在其他平台上可能又是不一样的了
- 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
- 这也就是我们一开始纠结的【15】到底还用不用的问题,这里给出解答,还是不确定,取决于平台
总结:跟结构相比,位段可以达到同样的效果,但是可以很好的节省空间,但是有跨平台的问题存在
- 这可能还有的老铁不太理解,举个例子:假设结构体A中的这个变量
a
只可能有【0】【1】【2】【3】这四种取值,那么只需要2个比特位就可以表达这四个数字了,即【00】【01】【10】【11】,那我们便可以使用位段来是实现:2
,但若是放在普通结构体中的话就只能是一个整型4个字节32个比特位的大小,这也就浪费了很多的空间 - 同理,若是变量
b
也只有5种表示形式的话,5个比特位就够了,c
、d
也是一样。那么这个时候位段就派上用场了,若是使用结构体的话就会浪费掉很多的空间。所以我们前面在看的时候,结构体所占的空间大小是16B,而位段只有8B
四、位段的应用
清楚了位段的相关知识和使用后,可能还是有同学比较迷惑这个位段到底是用来干嘛的,有什么实际应用场景吗?我们来看看
- 比方说这里有个IP数据包,有学习过《计算机网络》相关知识的读者应该都很清楚【不了解可以看看网络层知识点汇总】,我们平常在网络上和别人互相聊天的时候,所发送的消息并不是直接在网络链路上进行传送的,而是会将其封装到一个数据包中,它叫做IP数据包,例如我们所发送的
呵呵
只是里面的一个数据部分,还存在其他很多的字段,这些字段都占有各自的字节数 - 其实对于这些字节数来说,就是使用【位段】来实现,精准地控制好每个字段需要多少字节数,,就不会造成浪费的现象了
五、总结与提炼
最后来总结一下本文所学习的内容📖
- 在本文中,我们首先讲到了位段的相关概念,知道了原来使用结构体还可以实现位段,不过在看了二者的大小后,却产生了疑惑,为什么位段所占的大小是这些呢?
- 在清楚了位段在内容中的相关分布后,我带着读者一步步分析了位段中的成员数据到底是怎么一个个存放到内存中的,也通过VS中的【内存】验证观察了我们的分析结果,是正确的
- 然后便说道了位段这个东西其实具备很大的不确定性,因为它存在跨平台的问题,在不同平台下实现的机制可能不同,所以就会导致最后的位段大小会不一致
- 最后,也说道了位段的作用以及其实际的应用场景,让读者学以致用
以上就是本文要介绍的所有内容,感谢您的阅读,如果觉得有帮助的话,可以给个三连哦❤️❤️❤️
相关文章:

C生万物 | 十分钟带你学会位段相关知识
结构体相关知识可以先看看这篇文章 —— 链接 一、什么是位段 位段的声明和结构是类似的,有两个不同: 位段的成员必须是 int、unsigned int 或signed int位段的成员名后边有一个冒号和一个数字 在下面,我分别写了一个结构体和一个位段&…...

Spring Boot基础学习之(十):修改员工的信息
注意:spring boot专栏是一个新手项目,博文顺序则是功能实现的流程,如果有看不懂的内容可以到前面系列去了解。 本次项目所有能够使用的静态资源可以免费进行下载 静态资源 在本篇代码DAO层将通过Java文件去实现,在这里就不连接数…...
闭关十几天,我完成了我的毕业设计
个人简介 👀个人主页: 前端杂货铺 🙋♂️学习方向: 主攻前端方向,也会涉及到服务端(Node.js) 📃个人状态: 在校大学生一枚,已拿多个前端 offer(…...
认识rust的项目管理工具--cargo
cargo 提供了一系列的工具,从项目的建立、构建到测试、运行直至部署,为 Rust 项目的管理提供尽可能完整的手段。不过,我们无需再手动安装,之前安装 Rust 的时候(用rustup或者vscode加插件的方式安装)&#…...

面试常问的Linux之 I/O 复用
I/O 复用 一、I/O的概念 在Linux系统中,I/O(输入/输出)指的是计算机系统的数据交换过程,包括从外部设备读取数据(输入)和将数据发送到外部设备(输出)。I/O操作是Linux系统中非常重要…...

MySQL-binlog+dump备份还原
目录 🍁binlog日志恢复 🍂binlog介绍 🍂Binlog的用途 🍂开启binary log功能 🍂配置binlog 🍁mysqldump 🍂数据库的导出 🍂数据库的导入 🍁mysqldumpbinlog 🦐…...

互联网络-单级互联网络
1.立方体单级网络 1.定义 立方体单级网络(cube)的名称来源于下图所示的三维立方体结构,如010只能连接到000、011、110,不能直接连接到对角线上的001、100、101、111。 2.例题 1.编号为0、1、2、3、4,…,15的16个处理器,用单级互联网络互联,用Cube0互联函数时,与第10…...

上海亚商投顾:沪指四连阳重回3300点 中字头个股再发力
上海亚商投顾前言:无惧大盘涨跌,解密龙虎榜资金,跟踪一线游资和机构资金动向,识别短期热点和强势个股。 市场情绪大小指数今日走势分化,沪指低开后震荡反弹,创业板指盘中跌超1%。中字头个股再度发力&#x…...

LeetCode:150. 逆波兰表达式求值—栈
🍎道阻且长,行则将至。🍓 🌻算法,不如说它是一种思考方式🍀算法专栏: 👉🏻123 一、🌱150. 逆波兰表达式求值 题目描述:给你一个字符串数组 token…...

C/C++每日一练(20230410) 二叉树专场(4)
目录 1. 二叉搜索树迭代器 🌟🌟🌟 2. 验证二叉搜索树 🌟🌟🌟 3. 不同的二叉搜索树 II 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Golang每日一练 专…...

策化整理1
概述: 本游戏是一款恐怖类解密游戏,以反应毒品的危害和反对家庭暴力为主题 在游戏中玩家扮演被困入梦境内的主人公,寻找逃出梦境的方法 本游戏故事大背景: 主人公的父亲是一名毒贩,在母亲发现父亲开始吸毒后选择与父亲…...

【服务通信自定义srv调用3----客户端的优化】
客户端的优化 服务通信自定义srv调用,客户端随意提交两个数,完成数的相加。也就是实现参数的动态提交: 1.格式:rosrun xxxx xxxx 12 34 2.节点执行时候,需要获取命令中的参数,并且组织进 request 代码中应…...
React跨域解决方案
一、跨域日志报错 我们由于项目需要经常会需要对不同域名、不同子域的网站接口发起请求,有时甚至是对于同一域名的不同端口发起请求,此时我们经常看到以下报错: Access to XMLHttpRequest at xxx from origin xxx has been blocked by COR…...

内存五区的概念,内存池技术的诞生。
首先提出一道经典的面试题来引出今天的主角: 进程的虚拟空间分布是什么样的,全局变量放在哪里? 在数据初始化之后,全局变量放在.data段 在数据未初始化时,全局变量放在.bss段 内存五区 进程虚拟内存主要分为五个部分…...

力扣:字符串中的第一个唯一字符(C++实现)
题目部分: 解题思路: 方案一: 首先认真审题的小伙伴们一定会发现就是题目给了提示只包含小写字母,也就是说我们的排查范围是小写的26个字母。为了怕有的友友们一时短路想不起来,我就其按照顺序列出来吧。 即&#x…...

攻防世界 favorite_number mfw、[BJDCTF2020]ZJCTF,不过如此
favorite_number 进入环境得到源码 <?php //php5.5.9 $stuff $_POST["stuff"]; $array [admin, user]; if($stuff $array && $stuff[0] ! admin) {$num $_POST["num"];if (preg_match("/^\d$/im",$num)){if (!preg_match("…...

SummingMergeTree
假设有这样⼀种查询需求:终端⽤户只需要查询数据的汇总结果,不关⼼明细数据,并且数据的汇总条件是预先明确的(GROUP BY 条件明确,且不会随意改变)。 对于这样的查询场景,在ClickHouse中如何解决…...

JUC并发编程基础篇第一章之进程/并发/异步的概念[理解基本概念]
1. 进程和线程的概念 进程: 系统正在运行的一个应用程序;程序一旦运行就是一个进程;进程是资源分配的最小单位 线程: 是进程的实际运行单位;一个人进程可以并发控制多个线程,每条线程并行执行不同的任务 区别: 进程基本上相互独立的;而线程存在于进程内,是进程…...

c语言—指针进阶
创作不易,本篇文章如果帮助到了你,还请点赞支持一下♡>𖥦<)!! 主页专栏有更多知识,如有疑问欢迎大家指正讨论,共同进步! 给大家跳段街舞感谢支持!ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ ኈ ቼ ዽ ጿ…...
总结二分法
杨辉三角形(快速查找唯一值,mid型) //二分法解//流程:最大列->起点行->2k--n之间究竟哪一行(二分排列组合)->找到行数就等差数列对应位置#include<stdio.h> #include<stdlib.h>//注意排列组合的规律是建立在…...

eNSP-Cloud(实现本地电脑与eNSP内设备之间通信)
说明: 想象一下,你正在用eNSP搭建一个虚拟的网络世界,里面有虚拟的路由器、交换机、电脑(PC)等等。这些设备都在你的电脑里面“运行”,它们之间可以互相通信,就像一个封闭的小王国。 但是&#…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

MongoDB学习和应用(高效的非关系型数据库)
一丶 MongoDB简介 对于社交类软件的功能,我们需要对它的功能特点进行分析: 数据量会随着用户数增大而增大读多写少价值较低非好友看不到其动态信息地理位置的查询… 针对以上特点进行分析各大存储工具: mysql:关系型数据库&am…...
在Ubuntu中设置开机自动运行(sudo)指令的指南
在Ubuntu系统中,有时需要在系统启动时自动执行某些命令,特别是需要 sudo权限的指令。为了实现这一功能,可以使用多种方法,包括编写Systemd服务、配置 rc.local文件或使用 cron任务计划。本文将详细介绍这些方法,并提供…...

Mac软件卸载指南,简单易懂!
刚和Adobe分手,它却总在Library里给你写"回忆录"?卸载的Final Cut Pro像电子幽灵般阴魂不散?总是会有残留文件,别慌!这份Mac软件卸载指南,将用最硬核的方式教你"数字分手术"࿰…...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Axios请求超时重发机制
Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式: 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

NFT模式:数字资产确权与链游经济系统构建
NFT模式:数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新:构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议:基于LayerZero协议实现以太坊、Solana等公链资产互通,通过零知…...

selenium学习实战【Python爬虫】
selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...
A2A JS SDK 完整教程:快速入门指南
目录 什么是 A2A JS SDK?A2A JS 安装与设置A2A JS 核心概念创建你的第一个 A2A JS 代理A2A JS 服务端开发A2A JS 客户端使用A2A JS 高级特性A2A JS 最佳实践A2A JS 故障排除 什么是 A2A JS SDK? A2A JS SDK 是一个专为 JavaScript/TypeScript 开发者设计的强大库ÿ…...