【详解】C语言冷门知识点之--位段

文章目录

  • 一, 位段的解释
  • 二, 位段的声明和使用
    • 位段的声明:
    • 位段的使用:
  • 三,位段的空间大小计算
    • 第一个例子:
    • 第二个例子:
    • 注意:
  • 四, 位段的内存分配
  • 五,位段的跨平台问题
  • 六, 位段的应用

一, 位段的解释

下面是维基百科对位段的解释:

  位段(或称“位域”,Bit field)为一种数据结构,可以把数据以位的形式紧凑的储存,并允许程序员对此结构的位进行操作。这种数据结构的好处:

  • 可以使数据单元节省储存空间,当程序需要成千上万个数据单元时,这种方法就显得尤为重要。
  • 位段可以很方便的访问一个整数值的部分内容从而可以简化程序源代码。

  而位域这种数据结构的缺点在于,其内存分配与内存对齐的实现方式依赖于具体的机器和系统,在不同的平台可能有不同的结果,这导致了位段在本质上是不可移植的

二, 位段的声明和使用

  • 虽然位段可以决定用多少位来储存数据,但是切不可认为位段就是可以自定义一个数据类型。位段是依赖结构体来实现的,我们可以认为位段是可以将一个盒子里面格子自定义大小。
    在这里插入图片描述

位段的声明:

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

需要注意的是:

  • 这里面的数字代表的不是字节,是比特(bit)
  • 位段成员的类型只能是整型家族的,例如:int, unsigned int, signed int, char。

位段的使用:

int main()
{struct A a;a._a = 2;a._b = 3;a._c = 5;a._d = 10;return 0;
}

相当于实例化后的a里面的不同大小的内存里放入了数据。

三,位段的空间大小计算

因为不同平台上的规则都是不太一样的,计算出来的结果也会有些许差异,以下使用vs2022的x64环境下运行的
例如:

第一个例子:

#include <stdio.h>
struct A
{int _a : 2;//二进制位int _b : 5;int _c : 10;int _d : 30;
};int main()
{printf("%d", sizeof(struct A));return 0;
}

上面代码的输出结果是8

  • 声明类型是int类型的,所以一开始先开辟4个字节的内存,也就是32bit。
  • _a用掉了2bit,还剩下30bit。
  • _b用掉了5bit,还剩下25bit。
  • _c用掉了10bit,还剩下15bit。
  • _d需要30bit的空间,但是预先开辟的空间只剩下15bit,所以我们还需要再开辟一个int大小的空间,之前剩下的15bit的空间选择不使用,_d的30bit全放在第二个空间内。
  • 结果为8

第二个例子:

#include <stdio.h>
struct B
{int _a : 30;//二进制位int _b : 4;int _d : 32;
};int main()
{printf("%d", sizeof(struct B));return 0;
}
  • 声明类型是int类型的,所以一开始先开辟4个字节的内存,也就是32bit。
  • _a用掉了30bit,还剩下2bit。
  • 由于只剩下2bit,_b需要4bit,所以舍弃2bit,再开辟一个32bit空间, _b用掉了4bit,还剩下28bit。
  • 由于只剩下28bit,_d需要32bit,所以舍弃28bit,再开辟一个32bit空间,_d用掉了32bit
  • 总共开辟了3次int类型的空间,所以结果为12

注意:

  大家有没有发现,我们在声明位段的时候,如果定义的是int,那么冒号后面跟上的数字不能超过32,如果定义的是char,那么冒号后面跟上的数字不能超过8。如果超过以后,就会报出以下错误:
在这里插入图片描述

  其实根据内存对齐原则,如果超出以后,处理器就需要访问两次才能完整的得到数据。所以在定义的时候,应该避免超出应有的内存大小。

四, 位段的内存分配

  • 位段分配的内存中的比特位是从左向右使用的,还是从右向左使用的呢?
  • 如何证明内存分配剩余的比特位不够使用时,是继续使用还是浪费掉呢?

接下来我们分析:
用例代码:

#include <stdio.h>
struct A
{char _a : 3;char _b : 4;char _c : 5;char _d : 4;
};
int main()
{struct A a = {0};a._a = 10;a._b = 12;a._c = 3;a._d = 4;return 0;
}

我们假设:位段分配的内存中的比特位是从右向左使用的,分配剩余的比特位不够使用时,浪费掉剩余内存。
则:

  • 我们先定义位段,如下图:
    在这里插入图片描述
  • 执行程序:a._a = 10; 10的二进制为1010,放入_a中,由于_a只有3bit,需要截断,所以舍弃最高位1,放入010:

在这里插入图片描述

  • 执行程序:a._b = 12;,12的二进制为1100,刚好可以放入,如下图:
    在这里插入图片描述
  • 执行程序:a._c = 3;,3的二进制为11,由于_c有5bit,高位添0,放入00011,如下图:
    在这里插入图片描述
  • 执行程序:a._d = 4;,4的二进制为100,放入0100,如下图:
    在这里插入图片描述
  • 程序就基本执行完了,那么内存中是什么样的呢?根据上面分析,我们一开始给结构体初始化为0,我们可以得到:
    在这里插入图片描述
      由于机器是小端存储,所以内存上应该是:62 03 04.
    经过调试,可以看到:
    在这里插入图片描述

  以上也证明了, 在VS2022上,位段分配的内存中的比特位是从右向左使用的,分配剩余的比特位不够使用时,浪费掉剩余内存,重新开辟新的空间。

当然,不同平台得到的结果也可能会不同,这正是位段的缺点,可移植性差,接下来我们看看位段的跨平台问题。

五,位段的跨平台问题

  1. int 位段被当成有符号数还是无符号数是不确定的。
  2. 位段中最大位的数目不能确定。(16位机器最大16,32位机器最大32,写成27,在16位机器会出问题)。
  3. 位段中的成员在内存中从左向右分配还是从右向左分配的标准尚未定义。
  4. 当一个结构包含两个位段,第二个位段成员比较大,无法容纳打一个位段剩余的位时,是舍弃剩余的位还是利用,这是不确定的。

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

六, 位段的应用

位段由于跨平台的问题,真正的用途的其中一个是计网的IP数据报:
在这里插入图片描述

  😄 创作不易,你的点赞和关注都是对我莫大的鼓励,再次感谢您的观看😄

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/22211.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Visual Studio下2022Opencv的配置

Visual studio2022 opencv的配置 先从官网下载opencv &#xff1a; opencv releases 下载 ​ 我这里的开发环境是window版本&#xff0c;我们选择Windows版本进行下载 点开下载的文件&#xff0c;输入需要保存的路径 记住你保存的路径(我这里安装再F:) 安装需要时间静等安装…

基于单片机的盲人导航智能拐杖老人防丢防摔倒发短息定位

功能介绍 以STM32单片机作为主控系统&#xff1b; OLED液晶当前实时距离&#xff0c;安全距离&#xff0c;当前经纬度信息&#xff1b;超声波检测小于设置的安全距离&#xff0c;蜂鸣器报警提示&#xff1a;低于安全距离&#xff01;超声波检测当前障碍物距离&#xff0c;GPS进…

综合小实验

第一步&#xff1a;计划IP R1的环回&#xff1a;192.168.1.0/28 R2的环回&#xff1a;192.168.1.16/28 R123的O/O/0接口&#xff1a;192.168.1.32/28 R3-4&#xff1a;192.168.1.128/30 Vlan2&#xff1a;192.168.1.48/28 vlan3&#xff1a;192.168.1.64/28 192.168.1.0/24 0区…

深度学习——优化器Optimizer

代码以及详细注释&#xff1a; import torch import torch.utils.data as Data import torch.nn.functional as F import matplotlib.pyplot as plt# torch.manual_seed(1) # reproducible """超参数 """ # 学习率 LR 0.01 # 批大小 BATCH_…

Java中abstract关键字

文章目录 由来语法格式使用说明应用举例 由来 举例1&#xff1a; 随着继承层次中一个个新子类的定义&#xff0c;类变得越来越具体&#xff0c;而父类则更一般&#xff0c;更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象&#xff0c;以至于它…

【模式识别目标检测】——基于机器视觉的无人机避障RP-YOLOv3实例

目录 引入 一、YOLOv3模型 1、实时目标检测YOLOv3简介 2、改进的实时目标检测模型 二、数据集建立&结果分析 1、数据集建立 2、模型结果分析 三、无人机避障实现 参考文献&#xff1a; 引入 目前对于障碍物的检测整体分为&#xff1a;激光、红外线、超声波、雷达、…

【超全面】Linux嵌入式干货学习系列教程

文章目录 一、前言二、Linux基础篇三、数据结构与算法基础三、Linux应用篇四、Linux网络篇五、ARM篇六、Linux系统移植篇七、Linux驱动篇八、Linux特别篇九、Linux项目篇 一、前言 博主学习Linux也有几个月了&#xff0c;在这里为广大朋友整理出嵌入式linux的学习知识&#xff…

Matplotlib入门与实践(一)

Matplotlib 是一个 Python 的 2D绘图库&#xff0c;它以各种硬拷贝格式和跨平台的交互式环境生成出版质量级别的图形。通过 Matplotlib&#xff0c;开发者可以仅需要几行代码&#xff0c;便可以生成绘图&#xff0c;直方图&#xff0c;功率谱&#xff0c;条形图&#xff0c;错误…

oceanbase基础

与mysql对比 分布式一致性算法 paxos 存储结构&#xff08;引擎&#xff09;用的是两级的 数据库自动分片功能&#xff0c;提供独立的obproxy路由写入查询等操作到对应的分片 多租户 方便扩展 存储层 http://www.hzhcontrols.com/new-1391864.html LSM tree&#xff0c;is very…

渲染流程(上):HTML、CSS和JavaScript,是如何变成页面的?

在上一篇文章中我们介绍了导航相关的流程&#xff0c;那导航被提交后又会怎么样呢&#xff1f; 就进入了渲染阶段。这个阶段很重要&#xff0c;了解其相关流程能让你“看透”页面是如何工作的&#xff0c;有了这些知识&#xff0c;你可以解决一系列相关的问题&#xff0c;比如…

干货分享|SOLIDWORKS Composer如何解决缺失的actor?

​SOLIDWORKS Composer导入SOLIDWORKS模型&#xff0c;以便用户可以创建图形内容并与更广泛的受众共享项目。但是&#xff0c;有时模型导入时缺少Actor或组件&#xff0c;通常是由于在SOLIDWORKS中以轻量模式加载组件或Composer中的导入设置排除了曲面实体。 轻量模式 轻量模式…

学习C#基础知识和应用:

C#语言基础知识&#xff1a;了解C#的开发环境、变量、语法和程序结构等基础内容。这些知识是理解和开发C#自动化控制系统的前提。刚好&#xff0c;我这里有上位机入门&#xff0c;学习线路图&#xff0c;各种项目&#xff0c;需要留个6。 Winform窗体控件的应用&#xff1a;Wi…