【C语言】自定义类型:结构体深入解析(三)结构体实现位段最终篇

news2024/11/16 9:57:04

请添加图片描述

文章目录

  • 📝前言
  • 🌠什么是位段?
  • 🌉 位段的内存分配
  • 🌉VS怎么开辟位段空间呢?
  • 🌉位段的跨平台问题
  • 🌠 位段的应⽤
  • 🌠位段使⽤的注意事项
  • 🚩总结


📝前言

本小节,我们将学习结构体最后的知识:结构体实现位段,阿森将会和你一起去学习什么是位段?位段的内存分配,VS怎么开辟位段空间呢?位段跨平台问题,随即位段的应用,最后我们也要了解它的注意事项。文章干货满满,很容易理解,学习起来吧!😊


🌠什么是位段?

位段是C语言中结构体的一种数据类型。

位段允许在结构体中定义具有指定位数的成员,这些成员可以占用结构体变量内部的连续比特位

位段的声明和结构是类似的,有两个不同:

  1. 位段的成员必须是intusigned intsigned int,在C99中位段成员的类型也可以选择其他类型。

  2. 位段的成员后边有一个冒号和一个数字,这个数字代表了该成员变量在结构体内占用的bit位数。它用来限定成员变量的范围和存储空间。。

话不多说,给铁铁上两者比较代码:

struct A//位段
{
    int _a : 2;
    int _b : 5;
    int _c : 10;
    int _d : 30;
};
struct B//结构体
{
	int _a ;
	int _b ;
	int _c ;
	int _d ;
};


int main()
{
	printf("位段A大小=%d\n", sizeof(struct A));
	printf("结构体B大小=%d\n", sizeof(struct B));
	return 0;
}

输出:
在这里插入图片描述
分析:
在这里插入图片描述

首先看位段Struct A4个成员,如int _a:2这个成员中,int是类型,_a是变量名【变量名包含字母(大小写均可),数字(但不能以数字开头),下划线,如良好的变量名userNameorder_calculateResult】,:2指定该位段成员占用的bit位数为2bit,以此类推就会明白_b_c_d的组成情况。既然知道了他的组成,那计算他的大小吧,Struct A的大小和为47bit2+5+10+30=47bit),然后用编译器运行大小为8(这个8意思是八个字节,也等于8*8=64个比特位)。我们通过位段的一个成员一个成员加起来是47bit,而编译器计算出的是8个字节。

阿森小问:这8个字节是内存实际占用的吗?为什么编译器不显示47个bit,而是64bit,是不是跟结构体一样存在内存对齐呢?通过内存对齐来此应对内存的节约呢?阿森小答:没错,节省空间是没错,用的是也是同结构体一样的内存对齐的实现方式:字节对齐,不过方法不同。对于编译器来说,最小的内存单元是字节,它不会返回非整字节的bit数,因此它是按字节为单位返回,打印8个字节。位段成员总和47bit6字节(48bit)就可以了,怎么又要864bit)个字节了。通过结构体(128bit)与位段(64bit)对比,我们看出他的空间节省出来了,但是他不是无限制的节省空间,虽然节省了空间,但也有浪费,阿森一会讲解怎么浪费空间的。当然对于位段是要使用在特殊场景下,如在struct B中的int _a;假设他存储134,267这么大的整数那就不适合用位段,如果要存储0,1,2,32bit就可以完美的存储起来了。 0可以用00,1用01,2用10,3用11表示,而用int 存储可能需要32bit,节省了很多空间!那位段怎么实现内存分配,让47(bit)变成8(64bit)字节呢?

🌉 位段的内存分配

  1. 位段的成员可以是 intunsigned intsigned int 或者是 char 等类型。
  2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的⽅式来开辟的。
  3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使⽤位段。
    用代码理解:
struct A
{
    int _a : 2;
    int _b : 5;
    int _c : 10;
    int _d : 30;
};

阿森把宝图解分析:
在这里插入图片描述

首先_a的类型是int ,申请了4个字节,开辟32bit空间,_a需要2bit,到底是从右边开始使用,还是从左边使用这两个空间开始的,这个是不确定的,标准C语言并没有给规定,这取决于编译器,注:这不是大小端问题。假设它从右向左,分配2个空间给_a(绿色),然后再继续分配5个空间给_b(黄色),接着_c(蓝色)说我需要10bit,最后还剩下 15bit,接下来_d说我需要30个bit,15bit不够,内存说:那就再给你开辟一个整形32bit吧!然后他就存储完剩下的15bit,再存储新开辟的32bit里分配15bit继续存储,这是一种方式!当然也有第二种可能:剩下的我浪费掉,我不用,反正不够,那我在新开辟的空间里一些性存储完30个bit,这是不是一种方式。对于这个剩下的15个bit会不会使用,C语言有没有给规定,这也取决于编译器VS是一种实现,gcc是一种实现,这就说明了位段有很多不确定因素,位段是不跨平台的,位段是如何开辟空间的,是严格依赖编译器的!注重可以植平台应该避免使用位段,如果要使用,应该明白其开辟空间原理,避免造成不必要的麻烦!

🌉VS怎么开辟位段空间呢?

上代码来一起实战理解:

struct S
{
    char a : 3;
    char b : 4;
    char c : 5;
    char d : 4;
};

int main()
{
    struct S s = { 0 };
    s.a = 10;
    s.b = 8;
    s.c = 3;
    s.d = 4;
    printf("%d\n", sizeof(s));
    return 0;
}

代码运行:
在这里插入图片描述
阿森双手把宝图奉上:
在这里插入图片描述
图解分析:
首先一上来给s的成员都初始化为0,也就是每个bit都初始化为0s里的每个成员类型都是char,为了更好的理解他开辟的空间是什么样的?我们先开辟一个字节(8bit(两个黑色箭头处在同一字节处)),开辟好了,a3bit,是从2个黑色箭头往左使用,还是从开头往右使用的呢?剩余的空间不够了,是浪费,还是不浪费呢?这样子吧!我们先假设一种方案来:1. 从右向左使用,2.如果剩余的空间不够就直接使用下一个空间,浪费掉。

开始–>:先看两个箭头指向一个字节处,a10,用二进制位表示01010(注:在x86环境下,整数10二进制表示方式为0000 1010,这里为了方便看,简写5为就能理解了),a3bit,并没有把a全部存进去,从a取低位开始010,接着箭头移动格,然后b4bit,取1100,放进去,此时8bit只剩下1bit,根据我们定下的规则,如果剩余的空间不够,就浪费,使用下一个。好!接下来再开辟一个字节(8bit),黑色箭头指向下一个字节最右边,c你要5bit,好!一下子满足你,此时发现8bit只剩下3bit了又不够,好!编译器说:再给你在内存空间里弄一个字节(8bit)吧,d4bit,最后用了4bit,都存完了,总共3个字节。你可能说:有没有巧合呢?不充分吧!那阿森和你一起就调试起来看看内存和监视吧:

注意:在内存窗口我们看到是16进制存储方式,先把我们成员存储进去的bit进行16进制转换,再看内存。
拓展:2进制转16进制方式:
16进制的数字每⼀位是0~9, a ~f 的,0 ~9,a ~ f的数字,各⾃写成2进制,最多有42进制位就⾜够了,
如:2进制的01101011,换成16进制:0x6b,16进制表⽰的时候前⾯加0x
在这里插入图片描述
因此,我们把每个字节(8bit)划分24bit,然后再加上0x就可以;
第一个字节是前40110–>2^0+ 2^1+ 2^1+ 2^0=6,后4bit0010–> 2^0+ 2^0 +2^1 + 2^0=2,剩下的都是同样方法,00000011表示0x0300000100表示0x04,接下来看内存调试:
在这里插入图片描述
看出内存显示的确是62 03 04,一模一样。说明我们刚刚的方案是正确,符合VS的存储方式的:在一个字节内部存储数据从右向左使用,如果剩余的空间不够,就浪费。

代码输出:
在这里插入图片描述
分析结果:
这里可以看出开辟了3个字节,就可以把我们想存储的数据就存好了,如果没有位段的使用,用结构体要开辟4char类型,多出来一个字节,相对来说节省了空间。

当你读到这里,你已经明白了VS对位段的开辟是怎么样操作的,此时让我们给自己鼓个掌,送给自己,继续加油!
阿森和你再理清这3个字节是不是一次性开辟的存储数据,还是创建完一个字节存储数据,再创建一个字节再存储数据的。
用图更容易理解:

在这里插入图片描述

s是编译器一次性开辟好的,然后再存储数据,文章中为了更好的理解他的流程,所以用了一个字节开辟一个字节开辟的存储的数据!
在这里插入图片描述

内存调试也可以方便观察:按F10调试内存来看看,给内存输入&s,当调试s的成员进行初始化为0时,内存显示3个字节变红了,都为0,后面cc代表着还未被初始化,为随机值(经典烫烫烫),可以看出在给一个成员s开辟内存空间时,编译器是一下子分配好的,不是开辟一个字节空间就存储数据,内存调试图在下↓

在这里插入图片描述

🌉位段的跨平台问题

  1. int 位段被当成有符号数还是⽆符号数是不确定的。
  2. 位段中最⼤位的数⽬不能确定。(16位机器最⼤1632位机器最⼤32,写成27,在16位机器会出问题。
  3. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
  4. 当⼀个结构包含两个位段,第⼆个位段成员⽐较⼤,⽆法容纳于第⼀个位段剩余的位时,是舍弃
    剩余的位还是利⽤,这是不确定的。
    总结:
    跟结构相⽐,位段可以达到同样的效果,并且可以很好的节省空间,但是有跨平台的问题存在。

🌠 位段的应⽤

下图是⽹络协议中,IP数据报的格式,我们可以看到其中很多的属性只需要⼏个bit位就能描述,这⾥使⽤位段,能够实现想要的效果,也节省了空间,这样⽹络传输的数据报⼤⼩也会较⼩⼀些,对⽹络的畅通是有帮助的。

IP数据报(IP Datagram)是IP(Internet Protocol)网络层协议传输的数据单元

网络协议中ip数据报的格式:
在这里插入图片描述

IP数据报报头中的许多字段,其值的范围很小,只需要使用少量比特位就可以表示,这就适合使用位段表示。

比如说4位版本号版本号是不是给4bit就可以了?首部长度给4bit,服务类型给8bit,总长度给16bit,包括这个地方的标志位给上3bit就可以了,那像这种是不是实现这位段的形式更好一些?
什么叫ip数据报?简单地说一下,假设呢,你要聊天,说a要发一个信息给b
假设我们的使用微信,你在微信上发了一个元旦快来啦,之后,你就一下子就发到b手机上去了吗,你只要把它扔到网络上,就发到b的手机去了,不是的。
首先发送数据时,不仅仅发送原始数据,还需要封装额外的控制信息,如版本号、长度、源地址、目的地址等,组成完整的IP数据报,这些控制字段使用位段表示,精确占用需要的比特位数,可以最大限度节省空间。源地址和目的地址决定数据报发往哪里,避免误发

数据报大小合理,就像网络上车流量合理,可以提高传输效率(如果封装的13个数据都是int好比许多大车,传输效率慢,合理位段像不同的小车高效运行传输)
在这里插入图片描述
小尺寸的IP数据报更利于网络传输。因为网络传输的开销很大程度上取决于数据包的大小。

网络协议定义了数据报的格式,保证发送和接收双方都能正确理解数据内容。使用位段表示IP报头字段,可以有效减小IP数据报的大小,这对网络传输性能和通信效率都很有利。所以,位段就起到了一个很好的编解码方法,它可以帮助IP数据报更高效地使用报头空间,实现报头字段的最优编码。

这也是IP报头设计中广泛使用位段的重要原因。它可以很好地将IP数据报大小控制在一个合理范围内。

🌠位段使⽤的注意事项

位段的⼏个成员共有同⼀个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。

内存中每个字节分配⼀个地址,⼀个字节内部的bit位是没有地址的。所以不能对位段的成员使⽤&操作符,这样就不能使⽤scanf直接给位段的成员输⼊值,只能是先输⼊放在⼀个变量中,然后赋值给位段的成员。
代码:

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};

int main()
{
	struct A sa = { 0 };
	scanf("%d", &sa._b);//这是错误的
	return 0;
}

错误显示图:
在这里插入图片描述
正确方法:必须先将输入值存入有地址的普通变量中,然后赋值给位段成员。
例如先scanf输入一个整数到变量b,然后b的某几位赋值给位段成员。
正确代码:

struct A
{
	int _a : 2;
	int _b : 5;
	int _c : 10;
	int _d : 30;
};

int main()
{
	struct A sa = { 0 };
	//正确的⽰范
	int b = 0;
	scanf("%d", &b);
	sa._b = b;
	return 0;
}

代码运行显示可运行输入图:
在这里插入图片描述


🚩总结

这次阿森和你一起学习什么是位段? 位段的内存分配,VS怎么开辟位段空间呢?位段的跨平台问题,位段的应⽤,位段使⽤的注意事项,阿森将下一节和你一起学习联合体和枚举💗。

感谢你的收看,如果文章有错误,可以指出,我不胜感激,让我们一起学习交流,如果文章可以给你一个小小帮助,可以给博主点一个小小的赞😘,也可以点个小小的关注哦💘

请添加图片描述

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

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

相关文章

Hadoop安装笔记_单机/伪分布式配置_Hadoop3.1.3——备赛笔记——2024全国职业院校技能大赛“大数据应用开发”赛项——任务2:离线数据处理

将下发的ds_db01.sql数据库文件放置mysql中 12、编写Scala代码,使用Spark将MySQL的ds_db01库中表user_info的全量数据抽取到Hive的ods库中表user_info。字段名称、类型不变,同时添加静态分区,分区字段为etl_date,类型为String&am…

WPF项目创建HTTP WEB服务,不使用IIS业务 WPF桌面程序WebApi WPF 集成WebApi C# 创建HTTP Web API服务

在C# WPF应用程序中直接创建HTTP服务或WebAPI服务有以下优点: 自托管服务: 简化部署:无需依赖外部服务器或IIS(Internet Information Services),可以直接在应用程序内部启动和运行Web服务。 集成紧密&…

elasticsearch操作索引库

目录 一、创建索引库 二、查询索引库 三、删除索引库 四、修改索引库 一、创建索引库 ES中通过Restful请求操作索引库、文档。请求内容用DSL语句来表示。 创建索引库和mapping的DSL语法及示例如下: 二、查询索引库 查看索引库语法 GET /索引库名 三、…

vue3+elementPlus:el-drawer新增修改弹窗复用

在el-drawer的属性里设置:title属性&#xff0c;和重置函数 //html<!-- 弹窗 --><el-drawerv-model"drawer":title"title":size"505":direction"direction":before-close"handleClose"><el-formlabel-posit…

ios环境搭建_xcode安装及运行源码

目录 1 xcode 介绍 2 xcode 下载 3 xocde 运行ios源码 1 xcode 介绍 Xcode 是运行在操作系统Mac OS X上的集成开发工具&#xff08;IDE&#xff09;&#xff0c;由Apple Inc开发。Xcode是开发 macOS 和 iOS 应用程序的最快捷的方式。Xcode 具有统一的用户界面设计&#xff0…

Apache Flink连载(十八):Flink On Yarn运行原理及环境准备

🏡 个人主页:IT贫道_大数据OLAP体系技术栈,Apache Doris,Clickhouse 技术-CSDN博客 🚩 私聊博主:加入大数据技术讨论群聊,获取更多大数据资料。 🔔 博主个人B栈地址:豹哥教你大数据的个人空间-豹哥教你大数据个人主页-哔哩哔哩视频 目录 1. Flink On Yarn运行原理…

迅为RK3588开发板RTMP推流之视频监控之搭建 RTMP 媒流体服务器

1.安装 nginxrtmp 运行所要用到的库和依赖环境 apt-get update apt-get install build-essential libpcre3 libpcre3-dev libssl-dev zlib1g-dev openssl 2. 下 载 nginx-1.20.2 源 码 ( 下 载 地 址 &#xff1a; http://nginx.org/download/nginx-1.20.2.tar.gz) 和nginx-…

在Spring Cloud中使用Ribbon完成一个简单的负载均衡demo

Spring Cloud系列断更了有一段时间了&#xff0c;这段时间最近都在忙着项目上的事&#xff0c;天天修复bug以及调整需求&#xff0c;反正各种操劳&#xff0c;了解业务需求&#xff0c;然后开发相关功能&#xff0c;很久都没碰Spring Cloud系列的相关文章了&#xff0c;最近回头…

数据结构第0章 初识

名人说&#xff1a;莫听穿林打叶声&#xff0c;何妨吟啸且徐行。—— 苏轼《定风波莫听穿林打叶声》 本篇笔记整理&#xff1a;Code_流苏(CSDN)&#xff08;一个喜欢古诗词和编程的Coder&#x1f60a;&#xff09; 目录 0、思维导图1、数据结构1&#xff09;数据结构是什么&am…

如何取消ChatGPT Plus 订阅服务,不取消订阅有什么影响?取消了又如何再次订阅

一、前言 很多小伙伴给我反馈说自己不会取消chatgpt的plus订阅&#xff0c;今天就来给大家分享一下如何取消ChatGPTPlus订阅 ChatGPTplus快速订阅升级方法 二、不取消订阅有什么影响&#xff1f; 1、账号被风控&#xff08;有些可以使用3.5但是不能在升级4.0&#xff09; …

【Java】云HIS云计算医院信息平台源码

云HIS系统可实现“云部署”&#xff0c;即可实现在云计算下的多种部署模式。系统采用SaaS服务模式的新驱动&#xff0c;功能设计充分合理&#xff0c;界面布局合理美观&#xff0c;每个用户可具有个性化工作台。 一、系统特色 使用简易化 即开即用&#xff0c;快速复制&#…

前端实现websocket类封装

随着Web应用程序的发展&#xff0c;越来越多的人开始利用Websocket技术来构建实时应用程序。Websocket是一种在客户端和服务器之间建立持久连接的协议。这种协议可以在一个单独的连接上实现双向通信。与HTTP请求-响应模型不同&#xff0c;Websocket允许服务器自主地向客户端发送…

Flutter配置Android和IOS允许http访问

默认情况下&#xff0c;Android和IOS只支持对https的访问&#xff0c;如果需要访问不安全的连接&#xff0c;也就是http&#xff0c;需要做以下配置。 Android 在res目录下的xml目录中(如果不存在&#xff0c;先创建xml目录)&#xff0c;创建一个xml文件network_security_con…

八股文打卡day12——计算机网络(12)

面试题&#xff1a;HTTPS的工作原理&#xff1f;HTTPS是怎么建立连接的&#xff1f; 我的回答&#xff1a; 1.客户端向服务器发起请求&#xff0c;请求建立连接。 2.服务器收到请求之后&#xff0c;向客户端发送其SSL证书&#xff0c;这个证书包含服务器的公钥和一些其他信息…

Codeforces Round 917 (Div. 2)更新中...

A.Least Product(思维) 题意&#xff1a; 给出一个数组 a 1 , a 2 , . . . , a n a_1, a_2, ..., a_n a1​,a2​,...,an​&#xff0c;你可以进行若干次以下操作&#xff1a; 选择数组中的一个元素 a i a_i ai​&#xff0c;将这个数字修改为 0 ∼ a i 0 \sim a_i 0∼ai​之…

《Spring Cloud学习笔记:微服务保护Sentinel》

Review 解决了服务拆分之后的服务治理问题&#xff1a;Nacos解决了服务治理问题OpenFeign解决了服务之间的远程调用问题网关与前端进行交互&#xff0c;基于网关的过滤器解决了登录校验的问题 流量控制&#xff1a;避免因为突发流量而导致的服务宕机。 隔离和降级&#xff1a…

XIAO ESP32S3之物体检测加入视频流

一、前言 由于XIAO ESP32S3开发套件没有显示屏配件&#xff0c;因此加入http视频流功能&#xff0c;可通过浏览器请求ESP32S3上的视频流。 二、思路 1、XIAO ESP32S3启动后通过wifi连接到AP&#xff1b; 2、启动http服务器&#xff0c;注册get_mjpeg处理函数&#xff1b; 3…

判断电话号码是否重复-excel

有时候重复的数据不需要或者很烦人&#xff0c;就需要采取措施&#xff0c;希望以下的方法能帮到你。 1.判断是否重复 方法一&#xff1a; 1&#xff09;针对第一个单元格输入等号&#xff0c;以及公式countif(查找记录数的范围&#xff0c;需要查找的单元格&#xff09; 2…

Linux:不同计算机使用NFS共享资源

一&#xff0c;安装NFS文件系统 NFS即网络文件系统(network file system)&#xff0c;它允许网络中的计算机之间通过网络共享资源。目前&#xff0c;NFS只用于在Linux和UNIX主机间共享文件系统。 #使用mount命令可以将远程主机的文件系统 安装到 本地&#xff1a; #将远程主机…

2.3_3 进程互斥的硬件实现办法

2.3_3 进程互斥的硬件实现办法 1.中断屏蔽方法 利用“开/关中断指令”实现&#xff08;与原语的实现思想相同,即在某进程开始访问临界区到结束访问为止都不允许被中断,也就不能发生进程切换,因此也不可能发生两个同时访问临界区的情况&#xff09; 优点&#xff1a;简单、高效…