1.什么是位段

位段:以位为单位来定义结构体中的成员变量所占的空间内存。含有位段的结构体称为位段结构。
一个位段类型必须是int、unsigned int、signed int、char中的一种即整型,但不能是浮点型。
它的作用是利用最少的位数来存储数据,常用在通信协议方面等等。

2. 位段大小计算


#include <stdio.h>
struct S
{char a : 2;char b : 4;char c : 5;char d : 4;
};int main()
{struct S s = { 0 };s.a = 3;s.b = 12;s.c = 3;s.d = 4;printf("size=%d\n", sizeof(s));return 0;
}

第一次开辟1个字节(8位)的内存大小(不够再继续开辟)。
说明:第一次开辟的8个位中,a用去2个位,b用去4个,还剩2个位,但是c需要5个位,所以又开辟一个8位。
此时出现两种情况:
情况1、c用去上次开辟还剩的2个位,再用去新开辟的8个位中的3个位。
情况2、直接使用新开辟内存的5个位,上次剩的2个位被浪费。
C语言标准中没有被定义,所以不同的编译器会按照不同方式来计算。在VS2019编译器中,size是3,很显然,符合的是情况2,所以是被浪费掉的。

3. 位段的存储方式和内存分配

3.1存储方式

位段开辟内存空间的规则是:
1、按照4字节或者1字节来开辟。
2、位段涉及很多不确定因素,位段是不跨平台的,注重可移植的程序应该避免使用位段。(这一点非常重要!!!!!)
位段内存存储规则是:
1、一个位段存储单元能够全部储存位段结构中的所有成员,则该结构中所有成员只能放在一个位段存储单元里;反之,一个位段存储单元不能容纳下位段结构中的所有成员,则未被存储的成员从下一个位段存储单元开始存放。即不能跨字节存储。

typedef struct S1 {char a : 4;//下一个位段放在一个新的位段存储单元 ,所以该结构体占1+1=2字节char b: 5;
} S1;//占字节数:1+1=2

2、第二点是非常需要注意的点!!!
在很多地方都会出现以下类似的语句:
如果一个位段结构中只有一个占有0位的无名位段,则只占1或0字节的空间(C中是占0字节,而C++中占1字节);否则其他任何情况下,一个位段结构所占的空间至少是一个位段存储单元的大小。

但是,这句话是不够严谨的。因为没有具体说明是哪一个编译器产生的结果,而位段本身就因为C标准的未定义,而出现不同的结果。

接下来,请看以下程序分析。编译器环境:VS2019
程序1:

#include <stdio.h>
struct S
{char : 0;
};
struct S1
{char  : 4;
}S1;struct S2
{int : 0;
}S2;
struct S3
{int : 6;
}S3;int main()
{struct S s ;struct S1 s1;struct S2 s2;struct S3 s3;printf("size=%d\n", sizeof(s));printf("size1=%d\n", sizeof(s1));printf("size2=%d\n", sizeof(s2));printf("size3=%d\n", sizeof(s3));return 0;
}

编译后结果:

分析:通过验证发现,同char类型下,零位段的无名位段和非零位段的无名位段,所占字节数是不同的;同int类型下,零位段的无名位段和非零位段的无名位段,所占字节数是相同的。
结论:
1、位段结构中只存在一个零位段的无名位段,则所占空间应是一个位段存储单元即4字节。也可以理解一个int字节大小。
2、位段结构中只存在一个非零位段的无名位段,则所占空间应由位段类型决定。这里char类型就是1字节,int类型就为4字节。

程序2:

#include <stdio.h>
struct S
{char : 0;int : 0;
};
struct S1
{char  : 4;int : 6;
}S1;struct S2
{int : 0;char : 0;
}S2;
struct S3
{int : 6;char : 4;
}S3;int main()
{struct S s ;struct S1 s1;struct S2 s2;struct S3 s3;printf("size=%d\n", sizeof(s));printf("size1=%d\n", sizeof(s1));printf("size2=%d\n", sizeof(s2));printf("size3=%d\n", sizeof(s3));return 0;
}

编译后结果:

分析:这里S1和S3的结果都是8字节,是符合结构体对齐规则的(详情请看我的另外一篇博客)。而S和S2的结果,可以得到第三个结论;位段结构中只存在n个(n>0)零位段的无名位段,其所占空间都是一个位段存储单元(一个int字节大小)。

总结:
结论1:位段结构中只存在n个(n>0)零位段的无名位段,其所占空间都是一个位段存储单元(一个int字节大小)。
结论2:位段结构中只存在n个(n>0)非零位段的无名位段,则所占空间应由位段类型、结构体成员数量以及定义顺序决定。(需要具体程序具体分析)

另外,零位段的无名位段其作用是使下一个位段从下一个存储单元开始存放。

综上所述,第二点规则是在探索中发现的现象,个人总结仅供参考,感兴趣的朋友也可以自己尝试验证。虽然在实际应用中,没有这样的情况出现,但是这个现象还是蛮有意思的。最重要的一点:关于位段问题的探索,绝对不能抛弃编译器,空泛而谈!!!

3.2内存分配

根据前面的计算发现,一个位段存储单元有剩余位数但是存不下下一个位段时,剩余位数是会被浪费。以下用程序来分析位段的内存分配问题。

#include <stdio.h>
struct S
{char a : 3;char b : 4;char c : 6;char d : 4;
};
int main()
{struct S s = { 0 };s.a = 3;s.b = 12;s.c = 5;s.d = 4;printf("size=%d\n", sizeof(s));return 0;
}

这里需要考虑一个问题,位段结构中的数据要么从右往左,要么从左往右。
这里我们假设从右往左,得到如下图。给结构体成员赋值后,借助编译器,观察成员在内存的存放是否一致。

查阅内存结果如下:

这特别说明一点,不同编译器的数据存储方式存在大小端模式,还有字节序和位序不同的问题。通过验证发现,VS2019在我的电脑上是以小端模式存储。

4. 位段的跨平台问题

关于位段涉及的诸多问题都是由于C语言标准未定义,所以编译器的不同出现的情况也可能不同,所以位段不能跨平台,其原因主要有以下几点:
1、int位段被当成有符号数还是无符号数是不确定的。
2、位段结构中最大位的数目不能确定。(16位机器最大为16,32位机器最大为32)
3、位段结构中的成员在内存中是从左向右分配,还是从右向左分配尚未定义。
4、当一个结构包含两个位段,第二个成员位段比较大,无法容纳于第一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。
所以在考虑是否使用位段时要十分小心,因为跟普通的结构体相比,位段虽然可以达到同样的效果,并且可以很好地节省空间,但是存在跨平台的问题。

5. 位段使用注意事项

1、位段的类型只能是int,unsigned int,signed int、char这四种类型,不能是浮点型;
2、位段所占位数不能超过该基本类型所能表示的最大位数,比如 int是占4个字节,那么最多只能是32位

//注意事项2
typedef struct S1
{unsigned int a : 1;char  b : 2;    //存在一个非0位的位段,则至少占4字节或者1字节;一个位段存储单元的大小是指该成员的类型(uint或者char)所占字节数
}S1;//占字节数为:4+4=8字节

3、无名位段不能被访问,但是会占据空间,且无名位段有两种形式:零位段的无名位段和非零位段的无名位段。这里出现的结果可以很好的符合了前面的结论。

//注意事项3:无名位段的两种形式
typedef struct S2
{int : 0;int : 8;
}S2 ;//占字节数:4+4=8

4、不能对位段进行取地址操作,也不能出现数组的形式。
5、表达式中的位段会自动进行整型升级,自动转换为int型或者unsigned int。
6、对位段赋值时,最好不要超过位段所能表示的最大范围,否则可能会造成意想不到的结果。例如位段占2位,能表示的只有0、1、2、3,但给这个位段赋值却赋值为6,这是不可取的。

以上所述,皆为个人愚见,如有不妥之处,欢迎评论区指正或私信。

C语言——关于位段的理解相关推荐

  1. C语言结构体中__packed 和位段的理解!

    C语言中__packed 和位段的理解 一:__packed typedef __packed struct struct常用在数据结构中.而struct的字节对齐方式对于嵌入式底层的程序员来讲是必须 ...

  2. 如何理解c语言的变量,C语言中变量的理解.PDF

    C语言中变量的理解 年第 期 建建 电电 脑脑 2011 3 59 C 语言中变量的理解 潘 莉 河南经贸职业学院 东校区 河南郑州 ( 冤 450009 ) 摘 要 在很多 级语言的基础语法中都有变 ...

  3. 视频教程-C语言-从汇编角度理解C语言的本质-C/C++

    C语言-从汇编角度理解C语言的本质 擅长JavaWeb开发,游戏逆向外挂与反外挂,游戏保护对抗 孙冉 ¥49.00 立即订阅 扫码下载「CSDN程序员学院APP」,1000+技术好课免费看 APP订阅 ...

  4. 人工智能欠缺对语言和情感的理解和处理能力

    人工智能虽然能不断地学习以增加其运算和预测的准确性,但其还无法以相对固定的运算模式和逻辑能力来应对不可控的案情和社会情势.当某些合同条款.商业框架.案件情况在之前从未出现过时,人工智能将不可避免出现失 ...

  5. 关于C语言函数的简单理解

    关于C语言函数的简单理解 C语言中的函数 在C语言中,函数是构成程序的基本模块.程序的执行从main()函数的入口开始,到main()函数的出口结束,中间循环.往复.迭代的调用一个有一个函数.每个函数 ...

  6. 位在c语言中用什么定义,C语言中位段的详细介绍

    C语言中位段的详细介绍 位段(bit-field)是以位为单位来定义结构体(或联合体)中的成员变量所占的空间.含有位段的结构体(联合体)称为位段结构.采用位段结构既能够节省空间,又方便于操作.以下是百 ...

  7. 聊一聊C语言位域/位段

    目录 1.概念和定义 2.实例 在做嵌入式开发的时候,我们经常会遇到这样的代码: struct {unsigned int widthValidated : 1;unsigned int height ...

  8. c语言函数指针的理解与使用(学习)

    1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: 1 2 3 A) char * (*fun1)(char * p1,char * p2); B) char  ...

  9. visual c++ 6.0原版_C/C++编程笔记:C语言函数指针的理解与使用,就是这么简单明了!...

    1.函数指针的定义 顾名思义,函数指针就是函数的指针.它是一个指针,指向一个函数.看例子: 看看上面三个表达式分别是什么意思? C)这很容易,fun3是函数名,p1,p2是参数,其类型为char *型 ...

最新文章

  1. 使用Node.js写一个简单的api接口
  2. Python设计模式-代理模式
  3. Intel发布神经网络压缩库Distiller:快速利用前沿算法压缩PyTorch模型
  4. 单一课和综合课的划分依据_武夷岩茶产地如何划分?
  5. P1481 魔族密码 (LIS)
  6. NGenerics算法库是学习的好代码
  7. Hibernate学习基本配置
  8. plotplayer s/w hevc(h265)解码 问题
  9. 一年Java的阿里电话面试全纪录
  10. [高项]假设情景分析VS假设分析
  11. 有道云笔记不同步_有道云笔记不能无法同步解决教程
  12. Win10系统默认播放器一直被修改怎么解决
  13. 关于“DEP数据执行保护”的解决方案
  14. 日本“性爱机器人”上线1小时被抢空
  15. python os库的常用函数记录
  16. 安卓APP在运行时对全局进行网络状态监听的实现
  17. 短视频搬运规避检测技术-天问一号
  18. 利用python提取基因cDNA长度,exon数量,pep长度和PI
  19. 写一个块linux设备驱动
  20. Vue3 使用 element-plus 不生效的原因之一

热门文章

  1. 大话赛宁云 | 演系列-超仿真网络空间“演武场”
  2. js正则表达式校验值是否为一个数字(正负整数,正负小数均可校验)
  3. H3C交换机环路监测,NTP时间同步。
  4. 老外常用的网络英文缩写
  5. ResNext架构解析:深度神经网络的聚合残差变换
  6. 被动扫描、主动扫描的概念
  7. 人脸表情识别和情绪分类 | Python+TensorFlow(框架)+Keras+PyQt5
  8. 【地图可视化】Echarts地图上展示3D柱体
  9. 数一段字母中各个字母的个数
  10. 首个中文开源扩散模型!中文AI艺术时代即将开启(IDEA封神榜团队)