目录
🤩前言🤩:
🤯正文:位段🤯:
1.位段概述:
2.位段的内存分配:
3.位段的跨平台问题:
4.位段的应用:
🥳总结🥳:
🛰️博客主页:✈️銮同学的干货分享基地
🛰️欢迎关注:👍点赞🙌收藏✍️留言
🛰️系列专栏:💐【进阶】C语言学习
家人们更新不易,你们的👍点赞👍和👉关注👈真的对我真重要,各位路过的友友麻烦多多点赞关注,欢迎你们的私信提问,感谢你们的转发!
关注我,关注我,关注我,你们将会看到更多的优质内容!!
🏡🏡本文重点 🏡🏡:
🚅 位段概述 🚃 位段的内存分配 🚏🚏
🚅 位段跨平台问题 🚃 位段应用 🚏🚏
🤩前言🤩:
上节课中我们学习了有关结构体的声明、定义、初始化与自引用等语法结构,也学习了结构体内存对齐的大小计算方法,还学习了结构体传参的实际应用。经过这些知识的学习,我们对结构体各种功能的认识和使用就有了足够的了解。而今天我们要学习的,是学习完结构体后,对结构体内容的一点点补充,但也是结构体的一项很重要的能力——实现位段。
🤯正文:位段🤯:
在前面我们完整的学完了结构体的语法结构和使用方法,在学完结构体之后,我们就要研究研究结构体实现位段的重要能力了。
1.位段概述:
位段(bit-field)以位为单位来定义结构体(或联合体)中的成员变量所占的空间。含有位段的结构体(联合体)称为位段结构。采用位段结构既能够节省空间,又方便于操作。
位段的声明和结构体十分相似,但同时有两个不同点:
1. 位段的成员必须是 int 、signed int 、 unsigned int 或 char 类型。
2. 位段的成员名后边有一个冒号和一个数字(该成员所占内存空间大小,单位为 bit位)。
例如:
#include<stdio.h>
struct test
{
int _a : 2;
//成员 a 只占用 2 个比特位
signed int _b : 5;
//成员 b 只占用 5 个比特位
unsigned int _c : 10;
//成员 c 只占用 10 个比特位
char _d : 4;
//成员 d 只占用 4 个比特位
};
int main()
{
printf("The size of struct test is %d\n", sizeof(struct test));
return 0;
}
采用位段结构的好处是能够节省大量的空间,通过有条件地(根据实际使用需求)限制每个变量所占内存空间的大小,从而减少了整体结构的空间占用:
2.位段的内存分配:
位段存在的意义便是最大程度上去减少空间的浪费,所以在进行存储时,位段不会进行内存对齐操作。那么位段的内存空间是如何进行分配的呢?我们接下来就对位段的内存分配进行研究。
在这里各位小伙伴们一定要注意!位段的内存分配并没有严格的规则,在不同的编译器上产生的结果可能不同,我们今天的讲解,将以 Visual Studio 2022 为例进行研究。
我们同样首先需要知道位段进行内存分配的规则:
1. 位段的成员可以是 int 、unsigned int 、signed int 或者是 char(属于整形家族)类型
2. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
3. 位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。
4. 位段的内存分配是逐4字节(一个 int 类型的大小)进行分配的。
5. 当字节内空间不足以放下下一个成员变量时,剩余的空间不再使用,而是再从内存中申请一个字节的空间继续分配。
6. 不同类型(char 与 int类型)数据进行存储时将会另起4个字节(一个 int 类型的大小)进行存储。
就以我们上面的代码段(32位平台)为例,我们对其进行分析:
首先向内存申请4个字节(一个 int 类型)的空间开始分配,第一个成员 int 类型变量 _a 所占的内存空间为 2 比特位:
\ | 第一个4字节(一个 int 类型大小) | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
bit | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
变量 | _a |
接着,按照顺序将占据 5 比特位的第二个成员 signed int 类型变量 _b 存入:
\ | 第一个4字节(一个 int 类型大小) | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
bit | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
变量 | _a | _b |
然后继续依次将占据 10 比特位的第三个成员 unsigned int 类型变量 _c 存入:
\ | 第一个4字节(一个 int 类型大小) | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
bit | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
变量 | _a | _b | _c |
最后再根据规则,前三个成员类型均为 int 类型,而第四个成员类型为 char 类型,于是又向内存申请了第二个四字节(一个 int 类型)的空间,并向里面存入了占据 4 比特位的 char 类型变量 _d :
\ | 第一个4字节(一个 int 类型大小) | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
bit | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
变量 | _a | _b | _c |
\ | 第二个4字节(一个 int 类型大小) | |||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
bit | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 1516 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | |
变量 | _d |
至此,该位段结构的内存分配结束,共占据两个 int 类型数据的大小,即 8 个字节。
3.位段的跨平台问题:
我们上面说过,位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段,并且在未来位段结构的使用过程中,我们一定要提前仔细地研究好位段在不同编译器下使用时,究竟是如何进行内存分配的,再结合我们的实际需求实现跨平台使用。
而在位段进行跨平台使用时,我们通常需要注意以下四个关键点:
1. int 位段被当成有符号数还是无符号数是不确定的。
2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题)
3. 位段中的成员在内存中从左向右分配还是从右向左分配的标准尚未定义。
4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
总结下来,跟结构相比,位段可以达到跟结构相同的效果,并且可以更好的利用空间,但同时存在着跨平台问题。
4.位段的应用:
\ | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
20 字节 | 4位版本号 | 4位首部长度 | 8位服务类型 | 16位总长度 | ||||||||||||||||||||||||||||
16位标识符 | 3位标志 | 13位片偏移符 | ||||||||||||||||||||||||||||||
8位生存时间 | 8位协议 | 16位首部校验 | ||||||||||||||||||||||||||||||
32位源地址IP | ||||||||||||||||||||||||||||||||
32位目的地址IP | ||||||||||||||||||||||||||||||||
32位选项(如果有) | ||||||||||||||||||||||||||||||||
数据 |
🥳总结🥳:
到这里,我们关于位段这个内容的相关知识就介绍完毕了,再加上上一篇关于结构的讲解,我们关于结构体的所有内容的学习就全部结束了。希望能对各位小伙伴们关于结构的学习有所帮助,能为大家在将来的面试和工作中起到一些作用!也希望各位小伙伴们下去以后多多思考和练习,可以尝试在不同平台和不同编译器下运行,更全面的了解结构的原理和使用。
🔥🔥如果有天我们湮没在人潮中,庸碌一生,那是因为我们没有努力要活的丰盛🔥🔥
更新不易,辛苦各位小伙伴们动动小手,👍三连走一走💕💕 ~ ~ ~ 你们的点赞和关注对我真的很重要!最后,本文仍有许多不足之处,欢迎各位认真读完文章的小伙伴们随时私信交流、批评指正!