结构体位段问题

简介: 什么是位段位段的详细解释位段其实也是一种结构体的类型1.位段的成员是 int ,short int unsigned int , signed int , short , char 类型2.位段的成员名后有一个冒号和一个数字看一个例子:

什么是位段

位段其实也是一种结构体的类型

1.位段的成员是 int ,short int unsigned int , signed int , short , char 类型

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

看一个例子:

struct S
{
  int a : 2;
  int b : 5;
  int c : 10;
  int d : 30;
};
int main()
{
  struct S s = { 0 };
  printf("%zd\n", sizeof(s));
  //%zd--打印无符号整型
}

或许你猜的是47,我们来看一下结果


4e4066cb4fb1452f8ed9d03eea4fbe1b.png

打印出8的原因,在于下面:

其实 ,位段 -----位 ----二进制位

也就是说,冒号后面的数字,表示的是二进制数

我们先看整体,结构体中所有的成员都是int 类型,而int 类型是4字节,1字节是8个比特位,4字节就是32 比特位,

进入结构体内部,先创建了一个int类型的空间,

a占用2个比特位,b占用5个比特位,c占用10个比特位

abc在内存中的分布如下图:


a0e42af1bcbc411aa79a7e4a75f82dfe.png

但此时这4字节中,剩下的比特位不够30个,存放不了b,所以还需再开辟一个int类型的空间,即4个字节,来存放d

(因为成员类型是int 类型,刚才一次性开辟了4个字节的空间)

e5050499aeb44446add08cca12dad033.png


在内存中从右向左存储


所以,总体成员内存布局如上图:

这里你可能会有个疑惑,为什么不是在c的后面开始占用空间?

因为第一次开辟了4个字节(一个int类型)的空间,存放了a,b,c后,发现剩下的空间不够存放d,所以另外再开辟4个字节的空间,从这新开辟的空间中存放d。所以灰色的区域就是浪费了。


还有一个问题,位段后面的数字能大于32 吗?

其实是不行的,一个int类型是4字节,32比特位,不能大于32,如果强行大于32呢?

ed085a4f284344dfb639480b23c956f7.png

强行大于32就会报错。

所以位段是用来节省空间的

这里可能又有疑惑了,刚刚明明说浪费了灰色区域的空间,现在又说是节省空间,自相矛盾了。

你可以想想,如果不使用位段,那么就会开辟4个int类型的空间,就会开辟16字节的空间,但是使用位段,只开辟了8字节的空间,你说是不是节省空间呢?

再来看一个例子:

struct T
{
  char a : 3;
  char b : 4;
  char c : 5;
  char d : 4;
};
int main()
{
  struct T t = { 0 };
  printf("%zd\n", sizeof(t));
  t.a = 10;
  t.b = 20;
  t.c = 3;
  t.d = 4;
  printf("%d %d %d %d", t.a, t.b, t.c, t.d);
}

请问输出的结果分别是什么呢?

第二个printf可能你脱口就说出,分别打印10 20 3 4 嘛

这么简单,而第一个printf ,有了第一个例子的参照,我们可以计算t的空间大小

1d216689ec1645ec9fce5ce32cb7080b.pnga和b在内存中的布局如图,由于c占5个比特位,剩余空间不足c使用,再开辟一个char 的空间

如下图:

42624ca9954344e7bbb7ddd124d10852.png

剩下的3个比特位又不足够d使用,d需要4个比特位,故再开辟一个char类型的空间


07df0a320ed54cbdac2af445f045bae7.png

所以t的总大小是3个字节,第一个printf打印的是3,但是第二个printf真的是打印刚才说的

10 20 3 4 吗?

来看答案:

bfab435ec5c04035aee73d2ea28b2f1e.png

为什么会出现2 4 3 4 呢?好像毫不相干。

先听我解释:

t.a = 10,我们知道,a只需占用3个比特位, 而10 的二进制表示形式是 1010

占用3比特位,从右往左拿3 个,那就拿010,放入内存中。

如下图:

0d9beadd322442f9998af88d8e54e1fc.png

同理,t.b = 20, 20的二进制表示形式是 10100,从右往左,取4个,即0100,放入内存中,

t.c = 3 ,3的二进制表示形式是 011 ,从右往左,取5个,可是011不够五个呀,不够的那就补0就好了, 所以就取 00011 ,放入内存中,

t.d = 4 ,4 的二进制表示形式是 0100 ,放入内存中,所以,内存中的布局如图:


8af1c2c5e5e245beb627cb984422d5d0.png

如何观察到这个现象呢? 内存中是以16进制展现出来的,我们就先分析一下

fa80b59b52e34387882f92d3290c8b97.png

每4个二进制数就是一个16进制数,那么16进制数就是22 03 04 ,来验证结果:

image.gif

完美符合预期:

打印出来是 2 4 3 4 的原因是:

708ac1ea80b24cf2ac52c523eb32fd8f.png


a,b,c,d的二进制序列化为十进制后就如上图,故打印的是2 4 3 4。

看到这里,你应该明白了位段是什么

不过,位段这个东西,是没有官方的标准规定的,在不同的平台,位段的使用也不同。

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

相关文章
|
6月前
|
编译器 Linux C语言
详解结构体内存对齐及结构体如何实现位段~
详解结构体内存对齐及结构体如何实现位段~
|
存储 程序员 C语言
结构体,联合体与位段
结构体,联合体与位段
59 0
|
1月前
|
存储 编译器 C语言
自定义类型(二)结构体位段,联合体,枚举
本文介绍了C++中结构体的默认对齐数修改,位段的声明和使用,联合体的概念及其实际应用,以及枚举类型的用途。通过实例展示了如何优化内存使用和提高代码可读性。
17 1
|
3月前
|
存储 安全 C语言
结构体与联合体
结构体与联合体
28 0
|
6月前
结构体与共用体3
结构体与共用体3
28 0
|
6月前
|
机器学习/深度学习 C语言
结构体与共用体2
结构体与共用体2
32 0
|
6月前
|
存储 编译器 C语言
自定义类型:结构体(自引用、内存对齐、位段(位域))
自定义类型:结构体(自引用、内存对齐、位段(位域))
|
6月前
|
存储 编译器 C语言
结构体的内存对齐与位段
当我们描述一个人的年龄时我们可以使用,int age = 18;但是如果我们要描述一个人呢?很显然我们无法仅靠一个age就实现对一个人的描述,所以就有了结构体,在结构体中我们可以包含多种类型的数据,这样就可以实现对一个人的描述比如身高、爱好、体重等等
|
11月前
|
编译器 C++
结构体、枚举、位段、联合体详解
结构体、枚举、位段、联合体详解
70 0
|
存储 编译器 C语言
C/C++之自定义类型(结构体,位段,联合体,枚举)详解
C/C++之自定义类型(结构体,位段,联合体,枚举)详解
79 0