0


【自定义类型详解】第二篇——结构体实现位段

文章目录

前言

上一篇文章,我们一起学习了结构体,那学完了结构体,就一定得讲讲结构体实现位段的能力。
这篇文章,我们再来一起学习一个新知识——位段。
一起来学习吧!!!

1.什么是位段

首先,我们一起来了解一下什么是位段。

位段,C语言允许在一个结构体中以位为单位来指定其成员所占内存长度,这种以位为单位的成员称为“位段”或称“位域”( bit field) 。利用位段能够用较少的位数存储数据。

位段和结构体其实是非常相似的,但是有两个不同点:

  1. 位段的成员必须是
    char、int、unsigned int
    
    signed int
    
  2. 位段的成员名后边有一个冒号和一个数字。

举个例子:

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

A就是一个位段类型。

那里面的冒号和数字又表示什么呢?

首先我们要明白位段中的这个“位”字其实指的是二进制位。
我们知道一个二进制位就是1个比特位。
所以,A中

int _a : 2;

其实表示的就是
_a的大小是2

bit

;
同理:
_b的大小是5

bit

_c的大小是10

bit

_d的大小是30

bit

那我们再来思考一个问题:

位段A的大小应该是多少呢?
是30+10+5+2=47个bit吗,但我们知道sizeof计算出来的是字节数啊,所以会给它分配6个字节=48个比特位吗。

我们验证一下:
在这里插入图片描述
是8个字节哎,为什么呢?
别着急,我们接着往下看。

2.位段的内存分配

我们已经知道:

位段的成员可以是 int unsigned int signed int 或者是 char (属于整形家族)类型。

那位段所需要的空间是怎么来开辟的呢?

位段的空间上是按照需要以每次4个字节( int )或者1个字节( char )的方式来开辟的。

什么意思呢,解释一下:

就是说,如果位段的成员全部是整型的(位段成员一般都是同类型的),那上去就先给这个位段开辟4个字节的空间,如果不够用,放不下所有的成员,那就再开辟4个字节的空间,还不够用,继续开辟,以此类推。
如果成员全部是char类型的,那就一次开辟1个字节的空间,直至放得下所有成员。

好,那我想现在大家就明白为什么上面位段struct A的大小是8个字节了。

由于A的成员都是整型(int ),所以一次给A分配4个字节。
4个字节是32给比特位,A的前3个成员_a、_b、_c占了17个bit,32-17还剩15bit,但是A的第四个成员_d大小是30bit,而15<30不够。
怎么办?
再分配4个字节,这下就能放下_d,因此,struct A的大小是4+4=8个字节。

那现在又有一个问题:
对于刚才讨论过的struct A来说,上来先给它分配了4个字节及32比特位,前3个成员占了17个bit,32-17还剩15bit,然后这15bit留给第四个成员_d不够用,所以又开辟4个字节。

那这15bit以及新开辟的4个字节的空间是怎样给第四个成员_d分配的,有两种情况:

  1. _d的大小是30bit,它先使用了前面剩下的15bit,然后又使用了新开辟的4个字节中的15bit。
  2. 前面剩下的15bit,就直接浪费掉不用了,_d直接使用新开辟的4个字节中的30个bit。

那到底采用的是哪种呢?其实C语言的语法也并没有做出明确的规定:

位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。

3.验证vs环境下位段成员如何分配内存空间

虽然是不确定的,但是我们可以来验证一下,在我们当前使用的环境(vs2022)下,位段开辟的空间是怎么分配给每个成员的。
接下来,我们就通过一个实例来探究一下:

structS{char a :3;char b :4;char c :5;char d :4;};intmain(){structS s1 ={0};return0;}

我们先来思考一下,s1 的大小是多少,并猜测一下这些空间是如何分配给每个成员的。

s1 的每个成员都是char类型的,所以应该每次开辟1个字节。

  1. 首先分配的一个字节8个比特位,是可以放得下a,b的,我们假设是这样放的:在这里插入图片描述
  1. 再放c的话,只剩1个比特位,就不够了,所以需要再开辟1个字节的空间,那剩下的这1个比特位用不用呢,反正是不确定的,这里我们就假设再vs上不再使用剩下的这1个比特位了。在这里插入图片描述

3.那接着就要再开辟一个字节,因为还有一个成员
在这里插入图片描述

那vs环境下的位段变量s1的大小会是3个字节吗?
我们验证一下:
在这里插入图片描述
确实是3,证明我们的猜测大致是正确的。
接下来,我们再来进一步的验证一下:

intmain(){structS s1 ={0};
    s1.a =10; 
    s1.b =12; 
    s1.c =3; 
    s1.d =4;printf("%d",sizeof(s1));return0;}

我们先把位段变量s1 的成员都赋值为0,然后给他们重新赋一个非0值,最后,我们借助编译器观察一下,各个成员再内存中的存放是不是跟我们上面分析的一样。

在这里插入图片描述

在这里插入图片描述

现在我们通过编译器观察一下:
在这里插入图片描述
结果验证了在vs上位段的内存分配就是这样搞得,那在其它的平台上是不是也是这样呢?

不是的,前面已经提到了,这是不确定的,是标准未定义,在其他编译器上,可能结果就不一定是这样了。
位段是不跨平台的。

4.位段的跨平台问题

上面我们提到位段是不跨平台的,那接下来我们就来讨论讨论位段的跨平台问题。

  1. int 位段被当成有符号数还是无符号数是不确定的。
    什么意思呢,解释一下:

我们还能上面的代码来说

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

位段A的成员都是

int 

类型的,但是我们这样直接给一个

int 

,它**到底会被当成有符号

int 

还是无符号

int 

是不确定的**。

  1. 位段中最大位的数目不能确定。
    什么意思呢,再把上面的代码拿过来:
structA{int _a:2;int _b:5;int _c:10;int _d:30;};

这里面成员_d给的大小是30比特位。
但是在16位的机器上,int 类型的大小是2个字节=16个比特位,即在16位的机器上最大的位数才16位
那如果我们把

int _d:30;

的代码放在16位机器上,是不是就出错了啊。

  1. 位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。
    再来看一张上面用过的图:
    在这里插入图片描述

我们当时为什么这样放,是不是我们假设的啊,我们假设位段的成员再内存中是从右向左分配的。
为什么假设,因为这时标准未定义的,在不同的平台上可能就是不一样的。

  1. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的
    这个问题我们其实在上面也已经提到过了,再来看一张上面用过的图:
    在这里插入图片描述

这张图上,当剩余的空间无法容纳后面的位段成员时,我们就是把剩余的空间舍弃了,去使用新开辟的空间来放后面的成员,而在vs上也就是这样做的。
当然它也是不确定的,在不同平台可能不同

总结

跟结构体相比,有时候位段可以达到和结构体同样的效果,而且可以很好的节省空间,但是有跨平台的问题存在。

structA//位段{int _a :2;int _b :5;int _c :10;int _d :30;};structB//结构体{int a;int b;int c;int d;};intmain(){printf("%d\n",sizeof(structB));printf("%d\n",sizeof(structA));return0;}

比较一下它们的大小:
在这里插入图片描述

以上内容就是对结构体实现位段的一个讲解,欢迎大家指正!!!
在这里插入图片描述

标签: c++ 算法 c语言

本文转载自: https://blog.csdn.net/m0_70980326/article/details/127150039
版权归原作者 Y_^O^ 所有, 如有侵权,请联系我们删除。

“【自定义类型详解】第二篇&mdash;&mdash;结构体实现位段”的评论:

还没有评论