当前位置: 首页 > news >正文

浅谈C语言位段

1、位段的定义

百度百科中是这样解释位段的:

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

以下,我们均在VS2022的编译环境下去探讨和理解位段

2、位段的声明和使用

A、位段的声明

位段是与结构体相结合的,只有在结构体中才能使用位段,位段使用的根本目的是为了节省不必要消耗的内存空间。

这是位段的声明,这里有4个点需要注意:

1、上图中的1、2、3均是指比特位,即bit,一个二进制位

2、位段的适用类型只限于整型家族

3、同一个结构体中,使用位段时最好保持其中成员类型均相同

4、位段的大小不能超过该类型的实际大小,比如说对于char类型,最多只能是8个比特位,对于int类型,最多只能是32个比特位

B、位段的使用

以下是位段的简单使用:

3、位段的内存大小的计算

考虑到在不同的平台环境下,位段的内存大小计算有所不同,以下关于位段内存大小的计算是基于VS2022的编译环境:

例1:

在位段中,编译器看到char类型便会向内存中申请1个字节的空间。

a占去5个比特位,还剩3个比特位;b将这3个比特位占掉,此时第1个字节的空间用完。

紧接着的成员是char类型的c,又会开辟1个字节的内存空间,c占去4个比特位;

成员d要占去5个比特位,但是之前开辟的1个字节中仅剩4个比特位,不够用,所以将这4个比特位

舍去,重新开辟1个字节用以存储d,故最终该位段的大小为3个字节

例2:

VS编译器看到int类型的成员,便会向内存申请4个字节的空间

a占去1个比特位,还剩31个比特位;b在这剩余的31个比特位中占去30个比特位,还剩1个比特位;

c要占去4个比特位,剩余的1个比特位显然不够用,所以再开辟4个字节的内存空间,在这新开辟的4个字节中,c占到4个比特位

d则是在剩下的28个比特位中占20个比特位

所以,该位段的大小为8个字节.

4、位段在内存中的存储方式

考虑到位段在不同的编译器下存储方式有所不同,以下的讨论均基于VS2022。

探讨位段在内存中的存储方式,其本质是去探讨,在开辟的1个字节或4个字节的空间中,各个结构体成员在该空间内部是从左向右(低地址向高地址)存储的,还是从右向左(高地址向低地址)存储的。

我们这里先说结论:位段在内存中是从右向左存储的,即高地址向低地址存储的

以下进行相关验证:

struct S
{char a : 1;//1char b : 6;//001010char c : 4;//0101char d : 3;//011
};
//0001 0101     0011 0101
// 1     5       3    5 
int main()
{struct S s = { 0 };s.a = 1;//1s.b = 10;//1010s.c = 5;//101s.d = 11;//1011return 0;
}

将s初始化为0,则s的2个字节,16个比特位中均为0

在s的第1个字节空间中,从右向左存储,将a存储进去,存储的是1;b在初始化后6位二进制中放的是000000,在赋值后,存储的是001010,故第1个字节中存放的是 00010101

在s的第2个字节空间中,从右向左存储,将c存储进去,存储的是0101

由于d只有3个比特位的空间,故赋值11时会发生截断,实际存储到d中的是011,因此第2个字节中存放的是 00110101

将这2个字节转换为16进制表示则为 : 15  35

接下来我们通过内存窗口进行观察验证:

可以看到内存中存储的确实为15 35,说明在VS2022的编译环境下,位段在内存中确实是从右向左(高地址向低地址)存储的

5、位段的局限

位段有很多的局限,这些局限也就导致了位段的跨平台性很差:

1、位段中int类型是看成 signed int 还是 unsigned int 这是不确定的

2、位段中最大位的数目不能确定。位段的大小除了不能超过相应类型的实际大小外,还不能超过机器的位数。在16位的机器上,位段最大为16个比特位;而在32位的机器上,位段最大为32个比特位

3、位段在内存中究竟是从左向右存储,还是从右向左存储,这是标准未定义的

4、在1个结构体中,开辟的内存空间在容纳完1个位段后,剩余的位无法容纳第2个位段时,这些剩余的位是利用还是不利用,这是不确定的

综上所述,为了保证程序的可移植性,应尽量避免位段的使用。

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • springsecurity的学习(四):实现授权
  • 如何使用Wireshake解密Wi-Fi QoS Data报文?
  • 通过python搭建文件传输服务器;支持多台电脑之间互相传输文件(支持局域网或广域网)(应该也能用于虚拟机和宿主机之间)
  • Linux中的信号
  • LinuxC高级day05(函数指针、条件编译)
  • Python酷库之旅-第三方库Pandas(078)
  • Python知识点:如何使用Arcade进行简易游戏开发
  • 手机电量消耗分析工具 Battery Historian 指南
  • matlab求解方程
  • redis面试(十四)公平锁可重入
  • 【Linux入门】root密码忘记了怎么办?
  • 乳制品企业怎么防止信息泄露?使用加密软件保障数据安全
  • laravel 11 使用jw-auth进行API 登录
  • vs2022 启动之后崩溃解决方案
  • 学习嵌入式入门(十)高级定时器简介及实验(下)
  • JavaScript-如何实现克隆(clone)函数
  • 2018一半小结一波
  • 2019年如何成为全栈工程师?
  • Angular数据绑定机制
  • avalon2.2的VM生成过程
  • css系列之关于字体的事
  • es6
  • Flex布局到底解决了什么问题
  • happypack两次报错的问题
  • interface和setter,getter
  • Python进阶细节
  • ReactNative开发常用的三方模块
  • SpriteKit 技巧之添加背景图片
  • uni-app项目数字滚动
  • vue-loader 源码解析系列之 selector
  • Vue官网教程学习过程中值得记录的一些事情
  • 爬虫模拟登陆 SegmentFault
  • 如何学习JavaEE,项目又该如何做?
  • 使用权重正则化较少模型过拟合
  • 深度学习之轻量级神经网络在TWS蓝牙音频处理器上的部署
  • 3月7日云栖精选夜读 | RSA 2019安全大会:企业资产管理成行业新风向标,云上安全占绝对优势 ...
  • 扩展资源服务器解决oauth2 性能瓶颈
  • ​linux启动进程的方式
  • ‌前端列表展示1000条大量数据时,后端通常需要进行一定的处理。‌
  • #NOIP 2014# day.2 T2 寻找道路
  • #vue3 实现前端下载excel文件模板功能
  • #我与Java虚拟机的故事#连载05:Java虚拟机的修炼之道
  • (JSP)EL——优化登录界面,获取对象,获取数据
  • (pojstep1.1.1)poj 1298(直叙式模拟)
  • (python)数据结构---字典
  • (Redis使用系列) Springboot 使用redis的List数据结构实现简单的排队功能场景 九
  • (保姆级教程)Mysql中索引、触发器、存储过程、存储函数的概念、作用,以及如何使用索引、存储过程,代码操作演示
  • (二十九)STL map容器(映射)与STL pair容器(值对)
  • (一)springboot2.7.6集成activit5.23.0之集成引擎
  • (转) SpringBoot:使用spring-boot-devtools进行热部署以及不生效的问题解决
  • (转)淘淘商城系列——使用Spring来管理Redis单机版和集群版
  • (转)真正的中国天气api接口xml,json(求加精) ...
  • **Java有哪些悲观锁的实现_乐观锁、悲观锁、Redis分布式锁和Zookeeper分布式锁的实现以及流程原理...
  • .[backups@airmail.cc].faust勒索病毒的最新威胁:如何恢复您的数据?
  • .halo勒索病毒解密方法|勒索病毒解决|勒索病毒恢复|数据库修复