news 2026/8/14 14:28:33

C语言结构体中的位域

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C语言结构体中的位域

在硬件寄存器中一个字节往往分多个控制位,使用位域读写不用移位,编写代码简洁。那位域是什么?在什么情况下使用?使用需要注意什么呢?本文将一一展开。
一、位域简介
1、定义
位域(Bit Fields)是一种结构体的特殊成员,允许指定一个变量只占用若干个二进制bit位,而不是完整字节。用于在结构体中以位为单位分配存储空间,节省存储空间,特别适用于寄存器操作、协议解析、嵌入式系统等场景。
语法格式:
struct 结构体名{
类型 成员名 :占用位数;//后面数字=该成员占用二进制的位数
}
2、结构体选择位域还是普通char/int成员的原则
全部满足下面3个条件,优先使用位域,否则直接使用普通整型
a变量取值范围极小,只占用少量bit(1-12位以内)
b多个小字段挤在同一个字节/字,目标是节省内存/匹配硬件寄存器/匹配单字节协议位布局
c只在本地固件单一编译器运行,不需要跨编译器、跨平台传输二进制流。
二、位域的使用场景
1、使用场景
硬件寄存器读写(单片机、GNSS、485芯片寄存器)
芯片寄存器一个字节分多个功能标志位,每一位代表一个使能/状态,用位域直接映射寄存器,不用移位与运算。
通信协议报文
协议单个字节内拆分多个小字段,eg:1bit故障位,3bit功能码,不用手动移位拼接字节
大量布尔状态存储
多种故障状态,如果每个都用char需要几十个字节。
配置参数压缩
一些取值范围很小的参数(eg:速度配置只有7种状态,只需要3bit),无需完整字节存储。
2、具体示例
(1)基础标志位(故障状态集合)

#include<stdio.h>#include<windows.h>#include<math.h>structFaultBit{unsignedshortbat_low:1;//电量低unsignedshortwheel_lock:1;//后轮锁故障unsignedshortbat_lock:1;//电池锁故障unsignedshortmotor_err:1;//电机故障unsignedshortover_speed:1;//超速unsignedshortbrake_err:1;//刹车故障unsignedshortreserve:2;//保留位};intmain(){SetConsoleOutputCP(65001);SetConsoleCP(65001);structFaultBiterr;err.bat_lock=1;err.over_speed=1;printf("结构体大小:%zu字节\n",sizeof(err));printf("电池锁状态:%d\n",err.bat_lock);printf("后轮锁状态:%d\n",err.wheel_lock);return0;}

运行结果:

(2)用于特定协议:单字节分段

#include<stdio.h>#include<windows.h>#include<math.h>#pragmapack(push,1)structProtocolData{unsignedcharspeed_level:4;//速度档位0-15unsignedcharbike_state:3;//车辆状态unsignedcharfault_flag:1;//故障标志位};#pragmapack(pop)intmain(void){SetConsoleOutputCP(65001);SetConsoleCP(65001);structProtocolDatadata;data.speed_level=6;data.bike_state=1;data.fault_flag=0;printf("结构体大小:%zu字节\n",sizeof(data));printf("车辆速度等级:%d\n",data.speed_level);printf("车辆状态:%d\n",data.bike_state);printf("车辆故障状态:%d\n",data.fault_flag);return0;}

运行结果:

(3)硬件寄存器映射
单片机IO寄存器,8位寄存器每一位对应一个IO口高低电平

#include<stdio.h>#include<windows.h>#include<math.h>typedefstruct{unsignedchario0:1;unsignedchario1:1;unsignedchario2:1;unsignedchario3:1;unsignedchario4:1;unsignedchario5:1;unsignedchario6:1;unsignedchario7:1;}IO_REG;//将0x4000强制映射位IO_REG寄存器#defineIO_ADDR(*(volatileIO_REG*)0x4000)intmain(){IO_ADDR.io0=1;//访问0x4000地址里面的io0位域return0;}

(4)跨多字节位域和无名位域
当定义超过8bit,自动顺延至下一个字节
冒号前不写变量名,仅占位,用于协议保留bit,无法进行读写

structInfo{unsignedinta:5;unsignedinta:6;//超过8bit自动顺延下一个字节unsignedinta:7;unsignedint:6;//无名位域,填充保留位};

三、位域的存储
1、位域是按字节存储的,但不会跨字节(他们通常会放在同一个int或char变量的二进制中)
2、位域的大小受限于基础数据类型,如int或char,不能使用float、double等类型
3、位域的存储方式依赖编译器(大小端、高低位差异),不同CPU可能有不同大小端规则
GCC:低位放低bit
低版本Keil:高位在前,如果直接把位域结构体强转成字节发485/can,跨芯片、跨编译器会解析错误
所以:协议报文尽量不用位域,改用移位手动拼接。
四、位域使用
1、限制变量的值范围

2、调整字段顺序以优化存储
尽量控制1个字节可完成定义,相同类型写在一起

上图中变量a和b存放在一个char里,c存放不下了,放在第二个字节,d类型是int,偏移量在4的倍数上开始存储,所以需要填充2字节,本结构体共8字节。

位域成员的排列除结构体规则外,增加一条新规则:
相同类型、连续的成员在空间允许的情况下可以连续存放,不同类型的成员通常会跨单元存储,不同编译器处理可能不一样。

五、注意事项
1、位域基础类型只能是无符号unsigned,不能用于float、double
2、不能对位域取地址
位域内部没有存储对齐,bit没有独立的内存地址,指针最小寻址单位是字节
3、跨平台问题
位域的存储顺序依赖CPU大小端,不同平台可能不同。
不同编译器对齐可能不同,因此位域结构在不同平台上可能表现不一致。
4、位域成员不能是数组
5、超出bit范围会溢出,无自动报错
eg:某字段最大为15,但是填充为16直接截断变成0,运行静默出错,不会报警
6、跨字节自动填充,大小不直观
7、不要用于通用业务逻辑
仅硬件寄存器、通信协议、大量布尔标记;普通变量直接用char int,CPU操作位域需要额外移位运算,效率更低
8、无名位域仅占位,无法读写
用于协议保留位填充,只能填充不能赋值读取

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/8/14 14:26:50

告别官方全家桶:用 G-Helper 轻松掌控华硕笔记本性能控制

告别官方全家桶&#xff1a;用 G-Helper 轻松掌控华硕笔记本性能控制 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vivobook, Zenbook,…

作者头像 李华
网站建设 2026/8/14 14:20:41

从人体工学椅到密码管理器:我的高效生活与消费决策复盘

1. 从“沸点”到“沸点”&#xff1a;一次关于消费决策的深度复盘最近在社区里看到一个挺有意思的话题&#xff0c;叫“一人说一个你买过夯到爆的东西”。这个“夯”字用得特别传神&#xff0c;它不是简单的“好”&#xff0c;而是那种“买对了”、“超值”、“幸福感爆棚”的强…

作者头像 李华
网站建设 2026/8/14 14:15:04

两阶段最小二乘法TSLS结果解读:工具变量回归分析

两阶段回归TSLS分析结果解读一、方法概述两阶段最小二乘法&#xff08;Two-Stage Least Squares, 2SLS/TSLS&#xff09;是计量经济学中解决内生性问题最为经典的工具变量法之一。该方法由Theil和Basmann于1953-1957年间分别独立提出。2SLS方法分为两个阶段&#xff1a;第一阶段…

作者头像 李华
网站建设 2026/8/14 14:13:32

PT站一键转载脚本 auto_feed:把两小时的搬运活压缩到十分钟

PT站一键转载脚本 auto_feed&#xff1a;把两小时的搬运活压缩到十分钟 【免费下载链接】auto_feed_js PT站一键转载脚本 项目地址: https://gitcode.com/gh_mirrors/au/auto_feed_js 凌晨一点&#xff0c;我盯着两个浏览器窗口来回切换。左边是外站刚发布的蓝光原盘&am…

作者头像 李华
网站建设 2026/8/14 14:11:38

HAL库 vs LL库 vs 寄存器:到底用哪个?

STM32开发有三种方式&#xff0c;新手总纠结该学哪个。 这篇用同一个例子对比三种方式的代码量、效率、可移植性&#xff0c;最后给明确的选型建议。一、三种方式是什么 抽象层级&#xff08;从低到高&#xff09;&#xff1a;寄存器 ← 直接操作硬件地址&#xff0c;最底层↑…

作者头像 李华