自开发网站,网站网站制作需要多少钱,如何做线上销售,宿迁 网站制作文章目录 #x1f4dd;前言#x1f320;什么是位段#xff1f;#x1f309; 位段的内存分配#x1f309;VS怎么开辟位段空间呢#xff1f;#x1f309;位段的跨平台问题#x1f320; 位段的应⽤#x1f320;位段使⽤的注意事项#x1f6a9;总结 #x1f4dd;前言
本… 文章目录 前言什么是位段 位段的内存分配VS怎么开辟位段空间呢位段的跨平台问题 位段的应⽤位段使⽤的注意事项总结 前言
本小节我们将学习结构体最后的知识结构体实现位段阿森将会和你一起去学习什么是位段位段的内存分配VS怎么开辟位段空间呢位段跨平台问题随即位段的应用最后我们也要了解它的注意事项。文章干货满满很容易理解学习起来吧 什么是位段
位段是C语言中结构体的一种数据类型。
位段允许在结构体中定义具有指定位数的成员这些成员可以占用结构体变量内部的连续比特位。
位段的声明和结构是类似的有两个不同 位段的成员必须是int usigned int 或 signed int,在C99中位段成员的类型也可以选择其他类型。 位段的成员后边有一个冒号和一个数字这个数字代表了该成员变量在结构体内占用的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 A有4个成员如int _a:2这个成员中int是类型_a是变量名【变量名包含字母大小写均可数字但不能以数字开头下划线如良好的变量名userNameorder_calculateResult】2指定该位段成员占用的bit位数为2个bit以此类推就会明白_b_c_d的组成情况。既然知道了他的组成那计算他的大小吧Struct A的大小和为47bit25103047bit然后用编译器运行大小为8这个8意思是八个字节也等于8*864个比特位。我们通过位段的一个成员一个成员加起来是47bit,而编译器计算出的是8个字节。 阿森小问这8个字节是内存实际占用的吗为什么编译器不显示47个bit而是64个bit是不是跟结构体一样存在内存对齐呢通过内存对齐来此应对内存的节约呢阿森小答没错节省空间是没错用的是也是同结构体一样的内存对齐的实现方式字节对齐不过方法不同。对于编译器来说最小的内存单元是字节它不会返回非整字节的bit数因此它是按字节为单位返回打印8个字节。位段成员总和47bit6字节48bit就可以了怎么又要864bit个字节了。通过结构体128bit与位段64bit对比我们看出他的空间节省出来了但是他不是无限制的节省空间虽然节省了空间但也有浪费阿森一会讲解怎么浪费空间的。当然对于位段是要使用在特殊场景下如在struct B中的int _a;假设他存储134,267这么大的整数那就不适合用位段如果要存储0,1,2,3用2bit就可以完美的存储起来了。 0可以用001用012用10,3用11表示而用int 存储可能需要32bit,节省了很多空间那位段怎么实现内存分配让47bit变成864bit字节呢? 位段的内存分配
位段的成员可以是 int unsigned int signed int 或者是 char 等类型。位段的空间上是按照需要以4个字节 int 或者1个字节 char 的⽅式来开辟的。位段涉及很多不确定因素位段是不跨平台的注重可移植的程序应该避免使⽤位段。 用代码理解
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,15个bit不够内存说那就再给你开辟一个整形32个bit吧然后他就存储完剩下的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两个黑色箭头处在同一字节处开辟好了a占3个bit是从2个黑色箭头往左使用还是从开头往右使用的呢剩余的空间不够了是浪费还是不浪费呢这样子吧我们先假设一种方案来1. 从右向左使用2.如果剩余的空间不够就直接使用下一个空间浪费掉。 开始–先看两个箭头指向一个字节处a是10用二进制位表示01010注在x86环境下整数10二进制表示方式为0000 1010这里为了方便看简写5为就能理解了a要3bit并没有把a全部存进去从a取低位开始010接着箭头移动三格然后b要4bit取1100放进去此时8bit只剩下1bit,根据我们定下的规则如果剩余的空间不够就浪费使用下一个。好接下来再开辟一个字节8bit黑色箭头指向下一个字节最右边c你要5个bit,好一下子满足你此时发现8bit只剩下3bit了又不够好编译器说再给你在内存空间里弄一个字节8bit吧,d要4bit,最后用了4bit,都存完了总共3个字节。你可能说有没有巧合呢不充分吧那阿森和你一起就调试起来看看内存和监视吧 注意在内存窗口我们看到是16进制存储方式先把我们成员存储进去的bit进行16进制转换再看内存。 拓展2进制转16进制方式: 16进制的数字每⼀位是0~9, a ~f 的0 ~9,a ~ f的数字各⾃写成2进制最多有4个2进制位就⾜够了 如2进制的01101011换成16进制0x6b16进制表⽰的时候前⾯加0x 因此我们把每个字节8bit划分2段4bit然后再加上0x就可以 第一个字节是前4位0110–2^0 2^1 2^1 2^06后4bit为0010– 2^0 2^0 2^1 2^02剩下的都是同样方法00000011表示0x0300000100表示0x04接下来看内存调试 看出内存显示的确是62 03 04一模一样。说明我们刚刚的方案是正确符合VS的存储方式的在一个字节内部存储数据从右向左使用如果剩余的空间不够就浪费。 代码输出 分析结果 这里可以看出开辟了3个字节就可以把我们想存储的数据就存好了如果没有位段的使用用结构体要开辟4个char类型多出来一个字节相对来说节省了空间。
当你读到这里你已经明白了VS对位段的开辟是怎么样操作的此时让我们给自己鼓个掌送给自己继续加油 阿森和你再理清这3个字节是不是一次性开辟的存储数据还是创建完一个字节存储数据再创建一个字节再存储数据的。 用图更容易理解 s是编译器一次性开辟好的然后再存储数据文章中为了更好的理解他的流程所以用了一个字节开辟一个字节开辟的存储的数据
内存调试也可以方便观察按F10调试内存来看看给内存输入s当调试s的成员进行初始化为0时内存显示3个字节变红了都为0后面cc代表着还未被初始化为随机值经典烫烫烫可以看出在给一个成员s开辟内存空间时编译器是一下子分配好的不是开辟一个字节空间就存储数据内存调试图在下↓ 位段的跨平台问题
int 位段被当成有符号数还是⽆符号数是不确定的。位段中最⼤位的数⽬不能确定。16位机器最⼤1632位机器最⼤32写成27在16位机器会出问题。位段中的成员在内存中从左向右分配还是从右向左分配标准尚未定义。当⼀个结构包含两个位段第⼆个位段成员⽐较⼤⽆法容纳于第⼀个位段剩余的位时是舍弃 剩余的位还是利⽤这是不确定的。 总结 跟结构相⽐位段可以达到同样的效果并且可以很好的节省空间但是有跨平台的问题存在。 位段的应⽤
下图是⽹络协议中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怎么开辟位段空间呢位段的跨平台问题位段的应⽤位段使⽤的注意事项阿森将下一节和你一起学习联合体和枚举。
感谢你的收看如果文章有错误可以指出我不胜感激让我们一起学习交流如果文章可以给你一个小小帮助可以给博主点一个小小的赞也可以点个小小的关注哦