【C语言】位段(结构体实现位段)

目录

一、位段的定义

二、位段的声明

三、位段的内存分配

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

五、位段的优点

六、位段的跨平台问题

七、位段的应用

八、位段使用的注意事项


一、位段的定义

信息的存取一般以字节为单位。实际上,有时存储一个信息不必用一个或多个字节。

例如:"真"或"假"可以用0或1表示,只需1位即可。这时我们就可以用位段来进行存储。

那么什么是位段呢?

        位段(Bit Field)是C语言中的一种数据结构,它允许程序员在一个结构体中以位为单位来指定其成员所占的内存长度。这种以位为单位的成员称为"位段""位域"

        位段的定义要借助于结构体,即以二进制位为单位定义结构体成员所占存储空间,从而可以按"位"来访问结构体中的成员。

        位段与结构体形式与用法上是很相近的,但位段可以用来描述更为细腻的数据级别。

二、位段的声明

位段的声明语法形式如下:

struct   标签

{

    位段成员类型 位段成员名:分配内存的大小;

}

举例:

struct A
{int _a : 2;  //分配2bit的空间大小int _b : 5;	 int _c : 10;int _d : 30;
};
//A就是一个位段类型
//int:位段的成员类型   _a: 位段成员名   2:分配内存的大小 

注意:

  1. 位段的成员必须是(整型):int,unsigned int 或signed int,char
  2. 位段的成员名后边有一个冒号和一个数字
  3. 位段成员名中的 "_" 是可以是可以省略的,加上下划线与不加都可以,只是一种命名风格。
  4. 位段中分配内存的大小,宽度必须小于等于指定类型的位宽度。(即:冒号后面的数字的bit不能超过前面类型所占的bit
  5. 位段的位指的是二进制位
  6. 位段的声明应在结构体/联合体中。

    原因:

    位段是依赖结构体/联合体来实现的。在位段的声明和使用中,虽然可以决定用多少位来存储数据,但不能认为位段就是可以自定义的数据类型。可以理解为:位段是依赖于结构体实现的自定义类型。可以认为位段是将一个盒子里面的格子自定义大小。

三、位段的内存分配

位段所占内存大小为多少呢?

我们测试下面一段代码:

struct S
{char _a : 3;char _b : 4;char _c : 5;char _d : 4;
};int main()
{printf("%d\n", sizeof(struct S));return 0;
}

测试结果如图所示:

为什么会是3个字节(byte)呢?

原因:

  1. 位段的空间上是按照需要以4个字节( int )或者1个字节( char )的方式来开辟的。
  2. 一个位段必须存储在同一存储单元(即字)之中,不能跨两个单元。如果其单元空间不够,则剩余空间不用,从下一个单元起存放该位段。

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

我们看如下一段代码:

#include <stdio.h>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;
}

调试结果如下:

我们调试发现:

10,12,3,4在内存中是以16进制存放,为什么是62 03 04呢?

分析如下:

可知:位段中的成员在内存中是从右向左分配。

注意:

  1. 大小端指的是如果一个数据存储时超过一个字节的时候,才有字节顺序。这里是一个字节(内部),所以不谈顺序。
  2. 这里是先开辟一个字节,再开辟一个字节,最后再开辟一个字节,所以存放顺序一定是如图所示的存放方式。

五、位段的优点

 可以使数据单元节省储存空间,避免不必要的空间浪费。

 但是所谓节省空间是在一定程度上节省空间,并不是完全不浪费。

六、位段的跨平台问题

        1、int被位段作为是:无符号整数还是有符号整数,这个并没有做出明确的规定。

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

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

        4、当一个结构包含两个位段,假设第二个位段成员无法全部容纳于第一个剩余的位时,是把一个空间填满再放到新开辟的空间,还是直接全部放到新的空间,这个没有明确的规定。(vs中是直接全部放到新的空间)

补充:

  • 只有在位段的时候,int是没有确定是使用unsigned还是signed。除此之外int都是signed int。
  •  而char才是在使用和不使用位段的时候都是不确定的。

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

七、位段的应用

        1、在一些特定的应用场景中,需要对一个整数类型的变量中的每一位进行单独的控制或访问。例如,硬件寄存器常包含一些特定的位用于表示设备的状态,配置选项或标志位。使用位段可以让程序员更方便地访问和控制这些位,不需要进行位运算。

        2、在网络协议中,IP数据报的格式。可以看到其中很多属性只需要几个bit位就能描述,使用位段就能够实现想要的效果,也节省了空间,这样网络传输的数据报大小也会较小一些,对网络的畅通是有帮助的。

八、位段使用的注意事项

1、位段无地址,不能对位段进行取地址运算。

原因:

位段的几个成员共用一个字节,这样有些成员的起始位置并不是某个字节的起始位置,那么这些位置处是没有地址的。

因为内存中是每个字节分配一个地址,一个字节内部的bit位是没有地址的。

所以不能对位段的成员使用&操作符,这样就不能使用scanf直接对位段的成员输入值,只能是先输入一个值存放在一个变量中,然后再赋值给位段的成员。

如下所示:

#include <stdio.h>struct A
{int _a : 2;int _b : 5;int _c : 10;int _d : 30;
};int main()
{struct A sa = { 0 };scanf("%d", &sa._b); //这是错误的//正确的示范int b = 0;scanf("%d", &b);sa._b = b;return 0;
}

2、位段里的成员类型要尽量保持一致。否则会带来没必要麻烦,它内存开辟的时候可能会跟期望的不一样。

原因:

位段使用的场景本来就非常苛刻。如果再类型不一样,这样写出来的代码可控性就会变得差,而且它有许多不确定性,导致了它的不跨平台性。

3、位段在访问时与结构体访问方式相同,通过点操作(.)进行访问。访问时注意不要超出了所定义的位段大小。

4、两位段相邻时,相同数据类型的位段在编译过程中可以提高存储效率,而不同数据类型的位段则更可能需要考虑数据对齐而降低存储效率。

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

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

相关文章

匠心独运,B 端系统 UI 演绎华章之美

匠心独运&#xff0c;B 端系统 UI 演绎华章之美

上位机图像处理和嵌入式模块部署(f103 mcu获取唯一id)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 对于stm32f103系列mcu来说&#xff0c;一般每一颗原厂的mcu&#xff0c;都会对应一个唯一的id。那这个id可以用来做什么用呢&#xff1f;个人认为&…

ffmpeg 的sws_scale接口函数解析

ffmpeg 的 sws_scale 函数是 libswscale 库中的一个重要函数&#xff0c;用于进行图像的缩放和颜色空间转换。它的主要作用是将输入图像帧转换为另一种尺寸或颜色格式的输出图像帧。下面详细解析一下 sws_scale 函数的作用、参数等。 sws_scale 函数的作用 ffmpeg 的 sws_sca…

PX4 ROS2 真机

如果仿真跑通了。 真机遇到问题&#xff0c;可参考此文章。 ubuntu22 px4 1.14.3 ros2 humble 硬件接线。 先找两个usb - ttl串口&#xff0c;分别接到两台主机上&#xff0c;保证串口通信正常。 图中是个六合一的。浪费一天时间&#xff0c;发现是串口设置错误&#xff…

力扣 101. 对称二叉树

给你一个二叉树的根节点 root &#xff0c; 检查它是否轴对称。 /*** Definition for a binary tree node.* struct TreeNode {* int val;* struct TreeNode *left;* struct TreeNode *right;* };*/ bool check(struct TreeNode* L,struct TreeNode* R){if(!L&…

socket网络编程——套接字地址结构

一、通用 socket 地址结构 socket 网络编程接口中表示 socket 地址的是结构体 sockaddr&#xff0c;其定义如下&#xff1a; 1. #include <bits/socket.h> 2. 3. struct sockaddr 4. { 5. sa_family_t sa_family; 6. char sa_data[14]; 7. }; sa_family 成员是地址族类型…

【云原生】kubernetes中pod的生命周期、探测钩子的实战应用案例解析

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…

强国机械制造有限公司引入先进制造技术,提升产品质量和生产效率

强国机械制造有限公司2024年6月3日宣布引入了一系列先进制造技术,包括机器学习、人工智能和物联网等,旨在提升其产品的质量和生产效率。这些前沿技术的应用,使得公司的制造过程更加智能化和数据驱动,显著提高了产品的精度和稳定性。 通过机器学习算法,强国机械能够分析和预测生…

循环神经网络RNNLM续写“The meaning of life“

一、语料库 使用泰戈尔飞鸟集&#xff0c;作为一本英文诗集&#xff0c;用于续写The meaning of life比较好。 飞鸟集语料库如下 1 Stray birds of summer come to my window to sing and fly away. And yellow leaves of autumn, which have no songs, flutter and fall ther…

java注解能继承吗?可以的

注解继承 在 Java 中&#xff0c;注解&#xff08;Annotation&#xff09;默认是不具有继承性的&#xff0c;这意味着如果一个类或接口上使用了某个注解&#xff0c;其子类或实现类不会自动继承该注解。子类或实现类需要显式地重新声明该注解&#xff0c;如果希望它们也具有相…

springboot 解耦、隔离、异步的原则以及实战

在Spring Boot中实现解耦、隔离和异步的原则,能够提升应用程序的可维护性、可扩展性和性能。下面我会先介绍这三个原则的基本概念和意义,然后通过实战示例展示如何在Spring Boot应用中应用这些原则。 解耦 解耦是减少或消除应用程序组件之间依赖关系的过程,以提高模块的独…

《深入浅出C语言:从基础到指针的全面指南》

1. 简介 C语言是一种通用的编程语言&#xff0c;广泛应用于系统编程、嵌入式系统和高性能应用程序。它由Dennis Ritchie在1972年开发&#xff0c;并且至今仍然非常流行。C语言以其高效、灵活和强大的功能著称&#xff0c;是许多现代编程语言的基础。 2. 基本语法 2.1 Hello, …

VIKOR方法

简介 VIKOR方法是一种多标准决策&#xff08;MCDM&#xff09;或多标准决策分析方法。它最初由 Serafim Opricovic 开发&#xff0c;用于解决具有冲突和不可通约&#xff08;不同单位&#xff09;标准的决策问题&#xff0c;假设冲突解决可以接受妥协&#xff0c;决策者想要一…

C++中static关键字用法总结

在C中&#xff0c;关键字static有多种用途&#xff0c;它可以用于变量、函数。下面是static在不同上下文中的作用和举例。下面从static修饰的变量、函数三方面进行总结。 1、静态变量 静态变量分为全局变量、局部变量、函数中变量两种。 1.1 静态全局变量 静态全局变量声明在…

【文末附gpt升级秘笈】关于论文“7B?13B?175B?解读大模型的参数的论文

论文大纲 引言 简要介绍大模型&#xff08;深度学习模型&#xff09;的概念及其在各个领域的应用。阐述参数&#xff08;Parameters&#xff09;在大模型中的重要性&#xff0c;以及它们如何影响模型的性能。引出主题&#xff1a;探讨7B、13B、175B等参数规模的大模型。 第一…

前端框架前置知识之Node.js:Node.js入门

前端程序员有必要学 Node.js 吗&#xff1f;要学到什么程度&#xff1f; 小朋友&#xff0c;你是否有很多问号&#xff1f; 对于node.js&#xff0c;不知道你是否和我一样有很多问号&#xff1f; 其实在学习node.js之前&#xff0c;我已经学完了Vue框架&#xff0c;而且已经…

排序算法(C++)

参考C算法&#xff0c;这里面有些写法也值得商榷。 1. 冒泡排序算法 冒泡排序算法代码和思路比较简单&#xff0c;大家如果在面试时被要求实现排序时&#xff0c;可以用这种方法来实现。 该算法里&#xff0c;会统一地遍历待排序的数据&#xff0c;每次比较两个相邻的数据&a…

变现 5w+,一个被严重低估的 AI 蓝海赛道,居然用这个免费的AI绘画工具就能做!

大家好&#xff0c;我是画画的小强&#xff0c;致力于分享各类的 AI 工具&#xff0c;包括 AI 绘画工具、AI 视频工具、AI 写作工具等等。 但单纯地为了学而学&#xff0c;是没有任何意义的。 这些 AI 工具&#xff0c;学会了&#xff0c;用起来&#xff0c;才能发挥出他们的…

Java高级面试精粹:问题与解答集锦(六)

Java 面试问题及答案 1. 请解释Java中的多线程概念&#xff0c;并说明如何实现它&#xff1f; 答案&#xff1a; 多线程是指在Java程序中同时运行多个线程的功能。线程是程序执行的最小单元&#xff0c;Java中的多线程可以通过继承Thread类或实现Runnable接口来实现。 继承Th…

django中,出现ImportError: cannot import name ‘Mapping‘ from ‘collections‘错误

出现这个错误考虑与python版本不兼容的问题。colections 模块中的 mapping 子模块只在 Python 3.3以上版本 中才有&#xff0c;而我的Python版本是3.11&#xff0c;如果django的版本低于3.3&#xff0c;则会出现导入错误。那么如何在不卸载原有django的基础上进行更新版本呢&am…