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

C语言 位域

C 语言的位域(bit-field)是一种特殊的结构体成员,允许我们按位对成员进行定义,指定其占用的位数。

如果程序的结构中包含多个开关的变量,即变量值为 TRUE/FALSE,如下:

struct
{unsigned int widthValidated;unsigned int heightValidated;
} status;

这种结构需要 8 字节的内存空间,但在实际上,在每个变量中,我们只存储 0 或 1,在这种情况下,C 语言提供了一种更好的利用内存空间的方式。如果您在结构内使用这样的变量,您可以定义变量的宽度来告诉编译器,您将只使用这些字节。例如,上面的结构可以重写成:

struct
{unsigned int widthValidated : 1;unsigned int heightValidated : 1;
} status;

现在,上面的结构中,status 变量将占用 4 个字节的内存空间,但是只有 2 位被用来存储值。如果您用了 32 个变量,每一个变量宽度为 1 位,那么 status 结构将使用 4 个字节,但只要您再多用一个变量,如果使用了 33 个变量,那么它将分配内存的下一段来存储第 33 个变量,这个时候就开始使用 8 个字节。让我们看看下面的实例来理解这个概念:

#include <stdio.h>
#include <string.h>/* 定义简单的结构 */
struct
{unsigned int widthValidated;unsigned int heightValidated;
} status1;/* 定义位域结构 */
struct
{unsigned int widthValidated : 1;unsigned int heightValidated : 1;
} status2;int main( )
{printf( "Memory size occupied by status1 : %d\n", sizeof(status1));printf( "Memory size occupied by status2 : %d\n", sizeof(status2));return 0;
}

当上面的代码被编译和执行时,它会产生下列结果:

Memory size occupied by status1 : 8
Memory size occupied by status2 : 4

位域的特点和使用方法如下:

  • 定义位域时,可以指定成员的位域宽度,即成员所占用的位数。
  • 位域的宽度不能超过其数据类型的大小,因为位域必须适应所使用的整数类型。
  • 位域的数据类型可以是 int、unsigned int、signed int 等整数类型,也可以是枚举类型。
  • 位域可以单独使用,也可以与其他成员一起组成结构体。
  • 位域的访问是通过点运算符(.)来实现的,与普通的结构体成员访问方式相同。

位域声明

有些信息在存储时,并不需要占用一个完整的字节,而只需占几个或一个二进制位。例如在存放一个开关量时,只有 0 和 1 两种状态,用 1 位二进位即可。为了节省存储空间,并使处理简便,C 语言又提供了一种数据结构,称为"位域"或"位段"。

所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

典型的实例:

  • 用 1 位二进位存放一个开关量时,只有 0 和 1 两种状态。
  • 读取外部文件格式——可以读取非标准的文件格式。例如:9 位的整数。

位域的定义和位域变量的说明

位域定义与结构定义相仿,其形式为:

struct 位域结构名 
{位域列表};

其中位域列表的形式为:

type [member_name] : width ;

下面是有关位域中变量元素的描述:

元素描述
type只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型,决定了如何解释位域的值。
member_name位域的名称。
width位域中位的数量。宽度必须小于或等于指定类型的位宽度。

带有预定义宽度的变量被称为位域。位域可以存储多于 1 位的数,例如,需要一个变量来存储从 0 到 7 的值,您可以定义一个宽度为 3 位的位域,如下:

struct
{unsigned int age : 3;
} Age;

上面的结构定义指示 C 编译器,age 变量将只使用 3 位来存储这个值,如果您试图使用超过 3 位,则无法完成。

struct bs{int a:8;int b:2;int c:6;
}data;

以上代码定义了一个名为 struct bs 的结构体,data 为 bs 的结构体变量,共占四个字节:

对于位域来说,它们的宽度不能超过其数据类型的大小,在这种情况下,int 类型的大小通常是 4 个字节(32位)。

相邻位域字段的类型相同,且其位宽之和小于类型的 sizeo f大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止。

让我们再来看一个实例:

struct packed_struct {unsigned int f1:1;unsigned int f2:1;unsigned int f3:1;unsigned int f4:1;unsigned int type:4;unsigned int my_int:9;
} pack;

以上代码定义了一个名为 packed_struct 的结构体,其中包含了六个成员变量,pack 为 packed_struct 的结构体变量。

在这里,packed_struct 包含了 6 个成员:四个 1 位的标识符 f1…f4、一个 4 位的 type 和一个 9 位的 my_int。

让我们来看下面的实例:

#include <stdio.h>struct packed_struct {unsigned int f1 : 1;   // 1位的位域unsigned int f2 : 1;   // 1位的位域unsigned int f3 : 1;   // 1位的位域unsigned int f4 : 1;   // 1位的位域unsigned int type : 4; // 4位的位域unsigned int my_int : 9; // 9位的位域
};int main() {struct packed_struct pack;pack.f1 = 1;pack.f2 = 0;pack.f3 = 1;pack.f4 = 0;pack.type = 7;pack.my_int = 255;printf("f1: %u\n", pack.f1);printf("f2: %u\n", pack.f2);printf("f3: %u\n", pack.f3);printf("f4: %u\n", pack.f4);printf("type: %u\n", pack.type);printf("my_int: %u\n", pack.my_int);return 0;
}

以上实例定义了一个名为 packed_struct 的结构体,其中包含了多个位域成员。

在 main 函数中,创建了一个 packed_struct 类型的结构体变量 pack,并分别给每个位域成员赋值。

然后使用 printf 语句打印出每个位域成员的值。

输出结果为:

f1: 1
f2: 0
f3: 1
f4: 0
type: 7
my_int: 255
#include <stdio.h>
#include <string.h>struct
{unsigned int age : 3;
} Age;int main( )
{Age.age = 4;printf( "Sizeof( Age ) : %d\n", sizeof(Age) );printf( "Age.age : %d\n", Age.age );Age.age = 7;printf( "Age.age : %d\n", Age.age );Age.age = 8; // 二进制表示为 1000 有四位,超出printf( "Age.age : %d\n", Age.age );return 0;
}

当上面的代码被编译时,它会带有警告,当上面的代码被执行时,它会产生下列结果:

Sizeof( Age ) : 4
Age.age : 4
Age.age : 7
Age.age : 0

计算字节数:

#include <stdio.h>struct example1 {int a : 4;int b : 5;int c : 7;
};int main() {struct example1 ex1;printf("Size of example1: %lu bytes\n", sizeof(ex1));return 0;
}

以上实例中,example1 结构体包含三个位域成员 a,b 和 c,它们分别占用 4 位、5 位和 7 位。

通过 sizeof 运算符计算出 example1 结构体的字节数,并输出结果:

Size of example1: 4 bytes

对于位域的定义尚有以下几点说明:

  • 一个位域存储在同一个字节中,如一个字节所剩空间不够存放另一位域时,则会从下一单元起存放该位域。也可以有意使某位域从下一单元开始。例如:
struct bs{unsigned a:4;unsigned  :4;    /* 空域 */unsigned b:4;    /* 从下一单元开始存放 */unsigned c:4
}

在这个位域定义中,a 占第一字节的 4 位,后 4 位填 0 表示不使用,b 从第二字节开始,占用 4 位,c 占用 4 位。

  • 位域的宽度不能超过它所依附的数据类型的长度,成员变量都是有类型的,这个类型限制了成员变量的最大长度,: 后面的数字不能超过这个长度。

  • 位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。例如:

struct k{int a:1;int  :2;    /* 该 2 位不能使用 */int b:3;int c:2;
};

从以上分析可以看出,位域在本质上就是一种结构类型,不过其成员是按二进位分配的。

位域的使用

位域的使用和结构成员的使用相同,其一般形式为:

位域变量名.位域名
位域变量名->位域名

位域允许用各种格式输出。

请看下面的实例:

#include <stdio.h>int main(){struct bs{unsigned a:1;unsigned b:3;unsigned c:4;} bit,*pbit;bit.a=1;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */bit.b=7;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */bit.c=15;    /* 给位域赋值(应注意赋值不能超过该位域的允许范围) */printf("%d,%d,%d\n",bit.a,bit.b,bit.c);    /* 以整型量格式输出三个域的内容 */pbit=&bit;    /* 把位域变量 bit 的地址送给指针变量 pbit */pbit->a=0;    /* 用指针方式给位域 a 重新赋值,赋为 0 */pbit->b&=3;    /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */pbit->c|=1;    /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */printf("%d,%d,%d\n",pbit->a,pbit->b,pbit->c);    /* 用指针方式输出了这三个域的值 */
}

上例程序中定义了位域结构 bs,三个位域为 a、b、c。说明了 bs 类型的变量 bit 和指向 bs 类型的指针变量 pbit。这表示位域也是可以使用指针的。

相关文章:

C语言 位域

C 语言的位域&#xff08;bit-field&#xff09;是一种特殊的结构体成员&#xff0c;允许我们按位对成员进行定义&#xff0c;指定其占用的位数。 如果程序的结构中包含多个开关的变量&#xff0c;即变量值为 TRUE/FALSE&#xff0c;如下&#xff1a; struct {unsigned int w…...

【LeetCode热题100】【技巧】颜色分类

题目链接&#xff1a;75. 颜色分类 - 力扣&#xff08;LeetCode&#xff09; 只需排序三种&#xff0c;可以记录0和1的个数&#xff0c;然后直接原地赋值 class Solution { public:void sortColors(vector<int> &nums) {int zero 0, one 0;for (auto &num: n…...

笔记本电脑win7 Wireless-AC 7265连不上wifi6

1.背景介绍 旧路由器连接人数有限&#xff0c;老旧&#xff0c;信号不稳定更换了新路由器&#xff0c;如 TL-XDR5430易展版用户电脑连不上新的WIFI网络了&#xff0c;比较着急 核心问题&#xff1a;有效解决笔记本连接wifi上网问题&#xff0c;方法不限 2.环境信息 Windows…...

Linux gcc day5粘滞位

粘滞位 背景&#xff1a;一定时在一个公共目录&#xff08;root创建&#xff09;下。进行临时文件的操作 Linux系统中有很多人&#xff0c;我们需要在一个公共目录下&#xff0c;进行临时文件的操作&#xff08;增删查改&#xff09; 创建一个根目录下的dir&#xff08;mytmp…...

单片机按键消抖常用的软硬件方法

一&#xff1a;什么是开关抖动&#xff1f; 当我们按下按钮或拨动开关或微动开关时&#xff0c;两个金属部件会接触以短路电源。但它们不会立即连接&#xff0c;而是金属部件在实际稳定连接之前连接和断开几次。释放按钮时也会发生同样的事情。这会导致误触发或多次触发&#…...

钉钉自建应用-下载excel(h5)

由于不同手机对于文件下载有不同的支持&#xff0c;而且文件路径也不一样&#xff0c;找起来十分的麻烦。所以&#xff0c;最好是找到一个都支持的方法。还好&#xff0c;钉钉官网提供了网盘&#xff0c;我们可把文件保存到钉钉自带的网盘&#xff0c;这样方便查找。 这里需要…...

用二八定律分析零售数据,不就更直观了吗?

20%的商品贡献了80%的销售金额&#xff0c;你会不会想知道这些商品的销售金额、毛利、销售金额累计占比、毛利累计占比&#xff0c;会不会想知道这些商品在各个门店的销售表现&#xff1f;看是否能进一步提高销售金额&#xff0c;提高毛利。这样的报表该怎么做&#xff1f;奥威…...

NetSuite Saved Search-当前库存快照查询报表(二)

之前第一篇文章我们说明了&#xff0c;如何利用Saved Search来制作一个能够显示批次物料与非批次物料的Lot信息以及On Hand在手数量的“当前库存快照查询报表”&#xff0c;但是当用户提出“我们能否再加上批次物料的效期”需求时&#xff0c;我们原有的Saved Search并不能达到…...

【JavaSE】接口 详解(上)

前言 本篇会讲到Java中接口内容&#xff0c;概念和注意点可能比较多&#xff0c;需要耐心多看几遍&#xff0c;我尽可能的使用经典的例子帮助大家理解~ 欢迎关注个人主页&#xff1a;逸狼 创造不易&#xff0c;可以点点赞吗~ 如有错误&#xff0c;欢迎指出~ 目录 前言 接口 语法…...

嵌入式C基础——循环队列 ringbuffer 讲解

本期主题&#xff1a; 讲解ARRAY_SIZE的作用以及定义&#xff0c;还有一个踩坑分析 往期链接&#xff1a; 数据结构系列——先进先出队列queue数据结构系列——栈 stackLinux内核链表零长度数组的使用inline的作用嵌入式C基础——ARRAY_SIZE使用以及踩坑分析 目录 1. Ringbuff…...

【动态规划-状态压缩dp】【蓝桥杯备考训练】:毕业旅行问题、蒙德里安的梦想、最短Hamilton路径、国际象棋、小国王【已更新完成】

目录 1、毕业旅行问题&#xff08;今日头条2019笔试题&#xff09; 2、蒙德里安的梦想&#xff08;算法竞赛进阶指南&#xff09; 3、最短Hamilton路径&#xff08;《算法竞赛进阶指南》&模板&#xff09; 4、国际象棋&#xff08;第十二届蓝桥杯省赛第二场C A组/B组&#…...

全坚固笔记本丨工业笔记本丨三防笔记本相较于普通笔记本有哪些优势?

三防笔记本和普通笔记本在设计和性能方面存在显著差异&#xff0c;三防笔记本相较于普通笔记本具备以下优势&#xff1a; 三防笔记本通常采用耐磨、耐摔的材料&#xff0c;并具有坚固的外壳设计&#xff0c;能够承受恶劣环境和意外碰撞&#xff0c;有效保护内部组件不受损坏。相…...

机房搬迁方案

一、项目背景 随着XX公司业务的不断扩展&#xff0c;现有的机房设备已经无法满足日益增长的数据处理需求。同时&#xff0c;考虑到现有机房的设施老化及潜在的安全隐患&#xff0c;XX公司决定进行机房搬迁。本次搬迁旨在确保业务连续性、数据安全性以及新机房的高效运营。 二…...

推动科技创新润德生物邀您到场参观2024第13届生物发酵展

参展企业介绍 山东润德生物科技有限公司成立于2014年10月17日&#xff0c;是一家围绕生物制品的研发、生产、营销、国际贸易、技术服务为核心业务的国家高新技术企业&#xff0c;近年来荣获国家制造业单项冠军示范企业、国家级绿色工厂、国家知识产权优势企业、国家工业产品绿…...

如何在JavaScript中提高性能

在JavaScript中提高性能是一个涉及多个方面的任务&#xff0c;包括代码优化、数据结构选择、异步编程、避免全局查找、内存管理等。以下是一些关键的策略和技巧&#xff0c;可以帮助你提高JavaScript代码的性能&#xff1a; 1. 优化循环 使用for循环代替forEach&#xff0c;特…...

外观模式(面子模式)

外观模式 文章目录 外观模式什么是外观模式示例 什么是外观模式 外观模式(Facade),为子系统中的一组接口提供一个一致的界面&#xff0c;此模式定义了一个高层接口&#xff0c;这个接口使得这一子系统更加容易使用 Facade 外观类 知道哪些子系统类负责处理请求&#xff0c;将客…...

蓝桥杯考前复习三

1.约数个数 由乘法原理可以得出&#xff1a; import java.util.*; public class Main{static int mod (int)1e9 7;public static void main(String[] args){Map<Integer,Integer> map new HashMap<>(); //创建一个哈希表Scanner scan new Scanner(System.in);i…...

极客时间: 用 Word2Vec, LangChain, Gemma 模拟全本地检索增强生成(RAG)

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…...

VBA操作Word

检查word中的字体情况 Sub ListAllFontsInDocument()Dim doc As DocumentDim rng As RangeDim char As RangeDim fontName As StringDim uniqueFonts As Collection 初始化集合用于存储唯一字体名称Set uniqueFonts New Collection 获取当前活动文档Set doc ActiveDocument …...

Linux文件IO(4):目录操作和文件属性获取

目录 1. 前言 2. 函数介绍 2.1 访问目录 – opendir 2.2 访问目录 – readdir 2.3 访问目录 – closedir 2.4 修改文件访问权限 – chmod/fchmod 2.5 获取文件属性 – stat/lstat/fstat 2.5.1 文件属性 – struct stat 2.6 文件类型 – st_mode 3. 代码练习 3.1 要求 3.2 代…...

蓝牙 BLE 扫描面试题大全(2):进阶面试题与实战演练

前文覆盖了 BLE 扫描的基础概念与经典问题蓝牙 BLE 扫描面试题大全(1)&#xff1a;从基础到实战的深度解析-CSDN博客&#xff0c;但实际面试中&#xff0c;企业更关注候选人对复杂场景的应对能力&#xff08;如多设备并发扫描、低功耗与高发现率的平衡&#xff09;和前沿技术的…...

第25节 Node.js 断言测试

Node.js的assert模块主要用于编写程序的单元测试时使用&#xff0c;通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试&#xff0c;通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

【OSG学习笔记】Day 16: 骨骼动画与蒙皮(osgAnimation)

骨骼动画基础 骨骼动画是 3D 计算机图形中常用的技术&#xff0c;它通过以下两个主要组件实现角色动画。 骨骼系统 (Skeleton)&#xff1a;由层级结构的骨头组成&#xff0c;类似于人体骨骼蒙皮 (Mesh Skinning)&#xff1a;将模型网格顶点绑定到骨骼上&#xff0c;使骨骼移动…...

【Oracle】分区表

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 分区表基础概述1.1 分区表的概念与优势1.2 分区类型概览1.3 分区表的工作原理 2. 范围分区 (RANGE Partitioning)2.1 基础范围分区2.1.1 按日期范围分区2.1.2 按数值范围分区 2.2 间隔分区 (INTERVAL Partit…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

重启Eureka集群中的节点,对已经注册的服务有什么影响

先看答案&#xff0c;如果正确地操作&#xff0c;重启Eureka集群中的节点&#xff0c;对已经注册的服务影响非常小&#xff0c;甚至可以做到无感知。 但如果操作不当&#xff0c;可能会引发短暂的服务发现问题。 下面我们从Eureka的核心工作原理来详细分析这个问题。 Eureka的…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

Mysql8 忘记密码重置,以及问题解决

1.使用免密登录 找到配置MySQL文件&#xff0c;我的文件路径是/etc/mysql/my.cnf&#xff0c;有的人的是/etc/mysql/mysql.cnf 在里最后加入 skip-grant-tables重启MySQL服务 service mysql restartShutting down MySQL… SUCCESS! Starting MySQL… SUCCESS! 重启成功 2.登…...

打手机检测算法AI智能分析网关V4守护公共/工业/医疗等多场景安全应用

一、方案背景​ 在现代生产与生活场景中&#xff0c;如工厂高危作业区、医院手术室、公共场景等&#xff0c;人员违规打手机的行为潜藏着巨大风险。传统依靠人工巡查的监管方式&#xff0c;存在效率低、覆盖面不足、判断主观性强等问题&#xff0c;难以满足对人员打手机行为精…...