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

2024-06-04 04:12
文章标签 语言 实现 结构 位段

本文主要是介绍【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、两位段相邻时,相同数据类型的位段在编译过程中可以提高存储效率,而不同数据类型的位段则更可能需要考虑数据对齐而降低存储效率。

这篇关于【C语言】位段(结构体实现位段)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1029030

相关文章

基于 HTML5 Canvas 实现图片旋转与下载功能(完整代码展示)

《基于HTML5Canvas实现图片旋转与下载功能(完整代码展示)》本文将深入剖析一段基于HTML5Canvas的代码,该代码实现了图片的旋转(90度和180度)以及旋转后图片的下载... 目录一、引言二、html 结构分析三、css 样式分析四、JavaScript 功能实现一、引言在 Web 开发中,

SpringBoot中使用Flux实现流式返回的方法小结

《SpringBoot中使用Flux实现流式返回的方法小结》文章介绍流式返回(StreamingResponse)在SpringBoot中通过Flux实现,优势包括提升用户体验、降低内存消耗、支持长连... 目录背景流式返回的核心概念与优势1. 提升用户体验2. 降低内存消耗3. 支持长连接与实时通信在Sp

Conda虚拟环境的复制和迁移的四种方法实现

《Conda虚拟环境的复制和迁移的四种方法实现》本文主要介绍了Conda虚拟环境的复制和迁移的四种方法实现,包括requirements.txt,environment.yml,conda-pack,... 目录在本机复制Conda虚拟环境相同操作系统之间复制环境方法一:requirements.txt方法

Spring Boot 实现 IP 限流的原理、实践与利弊解析

《SpringBoot实现IP限流的原理、实践与利弊解析》在SpringBoot中实现IP限流是一种简单而有效的方式来保障系统的稳定性和可用性,本文给大家介绍SpringBoot实现IP限... 目录一、引言二、IP 限流原理2.1 令牌桶算法2.2 漏桶算法三、使用场景3.1 防止恶意攻击3.2 控制资源

springboot下载接口限速功能实现

《springboot下载接口限速功能实现》通过Redis统计并发数动态调整每个用户带宽,核心逻辑为每秒读取并发送限定数据量,防止单用户占用过多资源,确保整体下载均衡且高效,本文给大家介绍spring... 目录 一、整体目标 二、涉及的主要类/方法✅ 三、核心流程图解(简化) 四、关键代码详解1️⃣ 设置

Nginx 配置跨域的实现及常见问题解决

《Nginx配置跨域的实现及常见问题解决》本文主要介绍了Nginx配置跨域的实现及常见问题解决,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来... 目录1. 跨域1.1 同源策略1.2 跨域资源共享(CORS)2. Nginx 配置跨域的场景2.1

Python中提取文件名扩展名的多种方法实现

《Python中提取文件名扩展名的多种方法实现》在Python编程中,经常会遇到需要从文件名中提取扩展名的场景,Python提供了多种方法来实现这一功能,不同方法适用于不同的场景和需求,包括os.pa... 目录技术背景实现步骤方法一:使用os.path.splitext方法二:使用pathlib模块方法三

CSS实现元素撑满剩余空间的五种方法

《CSS实现元素撑满剩余空间的五种方法》在日常开发中,我们经常需要让某个元素占据容器的剩余空间,本文将介绍5种不同的方法来实现这个需求,并分析各种方法的优缺点,感兴趣的朋友一起看看吧... css实现元素撑满剩余空间的5种方法 在日常开发中,我们经常需要让某个元素占据容器的剩余空间。这是一个常见的布局需求

HTML5 getUserMedia API网页录音实现指南示例小结

《HTML5getUserMediaAPI网页录音实现指南示例小结》本教程将指导你如何利用这一API,结合WebAudioAPI,实现网页录音功能,从获取音频流到处理和保存录音,整个过程将逐步... 目录1. html5 getUserMedia API简介1.1 API概念与历史1.2 功能与优势1.3

Java实现删除文件中的指定内容

《Java实现删除文件中的指定内容》在日常开发中,经常需要对文本文件进行批量处理,其中,删除文件中指定内容是最常见的需求之一,下面我们就来看看如何使用java实现删除文件中的指定内容吧... 目录1. 项目背景详细介绍2. 项目需求详细介绍2.1 功能需求2.2 非功能需求3. 相关技术详细介绍3.1 Ja