写点什么

【结构体内功修炼】结构体实现位段(二)

作者:Albert Edison
  • 2022 年 10 月 06 日
    四川
  • 本文字数:1908 字

    阅读完需:约 6 分钟

【结构体内功修炼】结构体实现位段(二)

🌟 前言

上一篇文章讲解了 结构体内存对齐,这篇文章讲讲结构体实现位段的能力

1. 什么是位段

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


1、位段的成员必须是 intunsigned intsigned int


2、位段的成员名后边有一个冒号和一个数字。


📝 位段示例


struct A{  int _a : 2;  int _b : 5;  int _c : 10;  int _d : 30;};
复制代码


A 就是一个位段类型。


那位段 A 的大小是多少?


我们用 sizeof 看一下👇



为什么是 8 呢?


其实 位段 是指 二进制位


int _a : 2 的意思是:_a 只占 2bit 位;


int _b : 5 的意思是:_b 只占 5bit 位;


int _c : 10 的意思是:_c 只占 10bit 位;


int _d : 30 的意思是:_d 只占 30bit 位;


那把所有的 bite 位加起来也就 47bite 位,给 6(48 bit) 个字节完全够了呀!


为什么打印出来的是 8 个字节?也就是 64bit 呢?

2. 位段的内存分配

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


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


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


还是看刚刚这段代码


struct A{  int _a : 2;  int _b : 5;  int _c : 10;  int _d : 30;};
复制代码


首先,_a 是整型,不管三七二十一,先开辟 4 个字节也就是 32 bit 给它,但是 _a 只需要 2bit 呀,所以拿走 2 bit 以后,还剩 30 bit


然后 _b 需要 5 bit,所以拿走 5 bit 以后,还剩下 25 bit


其次 _c 需要 10 bit,所以拿走 10 bit 以后,还剩下 15 bit


最后 _d 需要 30 bit,但是此时还剩下 15 bit 呀,完全不够,那怎么办呢?此时再单独开辟 4 个字节也就是 32 bit_d 用(上面剩下的 15 bit 就不会再使用了);


所以 sizeof 求大小的时候,也就是 8 字节。


上面解释了 整型 的位段开辟,那如果当结构体的成员是 char 类型呢?


📝 代码示例


struct S{  char a : 3;  char b : 4;  char c : 5;  char d : 4;};
复制代码


首先,achar 类型,不管三七二十一,先开辟 1 个字节也就是 8 bit 给它,但是 a 只需要 3bit 呀,所以拿走 3 bit 以后,还剩 5 bit


然后 b 需要 4 bit,所以从 5 bit 里面拿走 4 bit 以后,还剩下 1 bit


其次 c 需要 5 bit,但是此时还剩下 1 bit 呀,完全不够,此时再单独开辟 1 个字节也就是 8 bitc 用,所以拿走 5 bit 以后,还剩 3 bit


最后 d 需要 4 bit,但是此时还剩下 3 bit 呀,完全不够,那么再单独开辟 1 个字节也就是 8 bitd 用(上面剩下的 3 bit 就不会再使用了);


所以 sizeof 求大小的时候,也就是 3 字节。


我们用 sizeof 看一下👇



既然学会了位段,那我们再来分析一下下面这段代码👇


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 = 12; s.c = 3; s.d = 4;
return 0;}
复制代码


上面我们已经计算过了,这个结构体开辟了 3 个字节,然后在主函数里面把这个 s 初始化为 0,这样就意味着,这 3 个字节里面,每一个 bit 位都为 0



首要为 a 开辟 1 个字节,a 只占其中的 3 bit,假设 a8 bit 中,是从低位向高位使用的,也就是从右向左的方向,现在把 10 赋值给 a10 的二进制是 1010,但是 a 的空间大小是 3 bit,所以只能存 3 bit,也就是存 010,最高位的 1 省去



b 里面要放 1212 的二进制是 1100,但是 b 刚好有 4 bit,所以直接放进去,此时还剩下 1 bit,完全不够 c 使用了,那么再开辟 1 字节👇



c5 bit,现在要把 3 放进去,3 的二进制是 011,但是 c 要占 5 bit 呀,所以前面添 0,也就是放 00011,此时还剩下 3 bit,完全不够 d 使用了,d4 bit 那么再开辟 1 字节👇



d4 bit,现在要把 4 放进去,4 的二进制是 100,但是 c 要占 4 bit 呀,所以前面添 0,也就是放 0100👇



此时主函数里面的代码已经走完了,那么内存里面到底存放的是什么呢?


由于二进制不好观看,我们换算成十六进制👇



那么内存里面到底存的是不是这个呢?我们可以调试看一看


3. 位段的跨平台问题

1、 int 位段被当成有符号数还是无符号数是不确定的。


2、位段中最大位的数目不能确定。(16 位机器最大 16,32 位机器最大 32,写成 27,在 16 位机器会出问题。)


3、位段中的成员在内存中从左向右分配,还是从右向左分配标准尚未定义。


4、当一个结构包含两个位段,第二个位段成员比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。


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

4. 位段的应用

那么位段应用在那些地方呢?比如下面这张网络中经典的 IP 数据报👇



发布于: 刚刚阅读数: 3
用户头像

Albert Edison

关注

目前在某大厂担任后端开发,欢迎交流🤝 2022.03.08 加入

🏅️平台:InfoQ 签约作者、阿里云 专家博主、CSDN 优质创作者 🛫领域:专注于C语言、数据结构与算法、C++、Linux、MySQL、云原生的研究 ✨成就:2021年CSDN博客新星Top9,算法领域优质创作者,全网累计粉丝4W+

评论

发布
暂无评论
【结构体内功修炼】结构体实现位段(二)_C语言_Albert Edison_InfoQ写作社区