首页 > 编程笔记 > C语言笔记 > C语言结构体和位操作
C语言位字段
位字段(bit-field)是一个由具有特定数量的位组成的整数变量。结构或联合的成员也可以是位字段。如果连续声明多个小的位字段,编译器会将它们合并成一个机器字(word)。这使得小单元信息具有更加紧凑的存储方式。当然,也可以使用位运算符来独立处理特定位,但是位字段允许我们利用名称来处理位,类似于结构或联合的成员。
位字段的声明格式为:
各部分的详细描述如下:
(1) 类型
指定一个整数类型,用来决定该位字段值被解释的方式。类型可以是 _Bool、int、signed int、unsigned int,或者为所选实现版本所提供的类型。这里的类型也可以包含类型限定符。
具有 signed int 类型的位字段会被解释成有符号数;具有 unsigned int 类型的位字段会被解释成无符号数。具有 int 类型的位字段可以是有符号或无符号的类型,由编译器决定。
(2) 成员名称
成员名称是可选的(可以不写)。但是,如果声明了一个无名称的位字段,就没有办法获取它。没有名称的位字段只能用于填充(padding),以帮助后续的位字段在机器字中对齐到特定的地址边界。
(3) 宽度
位字段中位的数量。宽度必须是一个常量整数表达式,其值是非负的,并且必须小于或等于指定类型的位宽。无名称位字段的宽度可以是 0。在这种情况下,下一个声明的位字段就会从新的可寻址内存单元开始。
当在一个结构或联合内声明一个位字段的时候,编译器会分配一个足以容纳它的可寻址内存单元。通常情况下,被分配的内存单元是一个 int 类型的机器字。
如果紧接着的位字段适合同一内存单元中剩下的空间,那么就被定义到与前面的位字段紧邻的位置。如果不适合的话,那么编译器就分配另外的内存单元,并在新单元的起始放置下一个位字段,或者跨过前一个内存单元的结尾和下一个内存单元的开头。
下面的例子重新定义了结构类型 struct Date,让其成员 month 和 day 只占据各自需要的位数。为了展示 _Bool 类型的位字段,我们为夏令时设定一个标签。这段代码以目标机器使用至少 32 位字为前提:
n 位的位字段可以有 2n 个不同的值。结构成员 month 的取值范围是 0~15;成员 day 的取值范围是 0~31;成员 year 的值范围是 -2097152~+2097151。我们可以使用常见的初始化列表方式初始化一个 struct Date 类型的对象:
对象 birthday 占据的存储空间大小与一个 32 位的 int 整数对象一样。和结构中其他成员所不同的是,位字段通常不会占据可寻址的内存位置,因此无法对位字段采用地址运算符(&)或宏 offsetof。
但是在其他方面,可以将位字段看作结构或联合成员,使用点和箭头运算符来获取,并以类似对待 int 或 unsigned int 变量的方式对其进行算术运算。因此,使用位字段重新定义的 Date 结构在函数 dateAsString()中不需作任何修改:
下面的语句为对象 birthday 调用函数 dateAsString(),并采用标准函数 puts()输出结果:
位字段的声明格式为:
类型[成员名称]:宽度;
各部分的详细描述如下:
(1) 类型
指定一个整数类型,用来决定该位字段值被解释的方式。类型可以是 _Bool、int、signed int、unsigned int,或者为所选实现版本所提供的类型。这里的类型也可以包含类型限定符。
具有 signed int 类型的位字段会被解释成有符号数;具有 unsigned int 类型的位字段会被解释成无符号数。具有 int 类型的位字段可以是有符号或无符号的类型,由编译器决定。
(2) 成员名称
成员名称是可选的(可以不写)。但是,如果声明了一个无名称的位字段,就没有办法获取它。没有名称的位字段只能用于填充(padding),以帮助后续的位字段在机器字中对齐到特定的地址边界。
(3) 宽度
位字段中位的数量。宽度必须是一个常量整数表达式,其值是非负的,并且必须小于或等于指定类型的位宽。无名称位字段的宽度可以是 0。在这种情况下,下一个声明的位字段就会从新的可寻址内存单元开始。
当在一个结构或联合内声明一个位字段的时候,编译器会分配一个足以容纳它的可寻址内存单元。通常情况下,被分配的内存单元是一个 int 类型的机器字。
如果紧接着的位字段适合同一内存单元中剩下的空间,那么就被定义到与前面的位字段紧邻的位置。如果不适合的话,那么编译器就分配另外的内存单元,并在新单元的起始放置下一个位字段,或者跨过前一个内存单元的结尾和下一个内存单元的开头。
下面的例子重新定义了结构类型 struct Date,让其成员 month 和 day 只占据各自需要的位数。为了展示 _Bool 类型的位字段,我们为夏令时设定一个标签。这段代码以目标机器使用至少 32 位字为前提:
struct Date { unsigned int month : 4; // 1是1月;12是12月 unsigned int day : 5; // 月份中的日(1~31) signed int year : 22; // (-2097152~+2097151) _Bool isDST : 1; // 如果是夏令时,则为true };
n 位的位字段可以有 2n 个不同的值。结构成员 month 的取值范围是 0~15;成员 day 的取值范围是 0~31;成员 year 的值范围是 -2097152~+2097151。我们可以使用常见的初始化列表方式初始化一个 struct Date 类型的对象:
struct Date birthday = { 5, 17, 1982 };
对象 birthday 占据的存储空间大小与一个 32 位的 int 整数对象一样。和结构中其他成员所不同的是,位字段通常不会占据可寻址的内存位置,因此无法对位字段采用地址运算符(&)或宏 offsetof。
但是在其他方面,可以将位字段看作结构或联合成员,使用点和箭头运算符来获取,并以类似对待 int 或 unsigned int 变量的方式对其进行算术运算。因此,使用位字段重新定义的 Date 结构在函数 dateAsString()中不需作任何修改:
const char *dateAsString( struct Date d ) { static char strDate[12]; sprintf( strDate, "%02d/%02d/%04d", d.month, d.day, d.year ); return strDate; }
下面的语句为对象 birthday 调用函数 dateAsString(),并采用标准函数 puts()输出结果:
puts( dateAsString( birthday ));
所有教程
- C语言入门
- C语言编译器
- C语言项目案例
- 数据结构
- C++
- STL
- C++11
- socket
- GCC
- GDB
- Makefile
- OpenCV
- Qt教程
- Unity 3D
- UE4
- 游戏引擎
- Python
- Python并发编程
- TensorFlow
- Django
- NumPy
- Linux
- Shell
- Java教程
- 设计模式
- Java Swing
- Servlet
- JSP教程
- Struts2
- Maven
- Spring
- Spring MVC
- Spring Boot
- Spring Cloud
- Hibernate
- Mybatis
- MySQL教程
- MySQL函数
- NoSQL
- Redis
- MongoDB
- HBase
- Go语言
- C#
- MATLAB
- JavaScript
- Bootstrap
- HTML
- CSS教程
- PHP
- 汇编语言
- TCP/IP
- vi命令
- Android教程
- 区块链
- Docker
- 大数据
- 云计算