9.3 位字段类型

计算机中一般以字节为单位来存储数据,但实际工作中,有时有些数据可以用一个字节中的一位或几位表示就可以。例如,电源是否接通,设备是否准备就绪,是否响应中断等,只需要一位中的两个状态就可以表示数据。对于这类问题,可以使用C语言提供的位字段类型数据。这也是高级语言的低级形式的一种表现。

9.3.1 位字段类型的定义

定义位字段类型的一般形式:

struct     结构体名
{
	unsigned [位字段名]:		常量表达式;
	......
};
或
struct
{
	unsigned [位字段] : 常量表达式;
	......
};

说明:
(1)C语言中没有专门的位字段类型,而是借助于结构体类型,以二进制位为单位来说明结构体中位字段成员所占空间。
(2)常量表达式用来指定每个位字段额宽度。需要指出的是,位字段结构体在存储时使用的内存空间大小与 int 数据相同,即使位字段结构体中各成员项的位数总和小于 int 型的位长,它也占用一个 int 型位长的内存空间。不同机型,int 型位长不同。当成员项总位长超过 int 型时,它将占用下一个连续的 int 位长空间,但不允许任何一个成员项跨越两个 int 空间的边界。例如,在 16 位的机器上,下面的定义是错误的:

struct packed_data
{
	unsigned a: 17;
	unsigned b: 1;
};

(3)省略位字段名时,称该字段为无名位字段。无名位字段起分隔作用。
(4)当无名位字段宽度为 0 时,其作用是使下一个位字段从下一个字节开始存放。
(5)位字段的宽度不得超过机器字长。
(6)在位字段结构中,不一定必须是位字段成员,也可以包含非位字段成员。
(7)不能定义位字段数组。

9.3.2 位字段变量的说明及其使用

1. 位字段变量的定义
位字段一经定义后,就可以说明位字段类型变量。位字段变量的说明与结构体变量和共用体变量定义相似,也存在三种方式。以直接定义方式为例,位字段结构体变量说明的一般形式:
struct 结构体名
{
unsigned [位段名] : 常量表达式;

} [变量名表];

例如,定义如下的位字段类型:

struct bit
{
	unsigned a : 2;
	unsigned b : 2;
	unsigned c : 1;
	unsigned d : 1;
	unsigned e : 2;
} data;

说明:
(1)位字段变量的使用形式与结构体相同。与结构体、共用体的成员访问相同,用圆点 “.” 运算符表示。位字段可以进行赋值操作,所赋之值可以是任意形式的整数。当其值超过位字段所允许的最大值时,为值溢出,系统不提示错误,但取值的低位。
(2)位字段可以按整型格式描述符输出(%d, %u, %o, %x 等),不能对位字段求地址,因此也不能读入位字段值,不能用指针指向位字段。
(3)共用体的类型说明中可以包含位字段。
2. 位字段变量的使用
例9.8 定义一个位字段类型,统计并整理考试成绩,输出相关考试信息
分析:假如将学生分别存在数组中的笔试成绩和上位机成绩统计并整理出考试是否合格。用位字段来存储计算机等级考试的考号、笔试成绩、上机成绩、笔试合格、上机合格、总评合格等,输出考试成绩以及是否合格信息。根据题目需要,定义如下结构体。
具体程序如下:

#include <stdio.h>
struct data
{
	long num;				/* 结构体成员,非位字段成员,存考号 */
	unsigned a : 7;		/* 位字段,7 位二进制可以存小于 128 的数,用来存笔试成绩 */
	unsigned b : 7;		/* 位字段,7 位二进制可以存小于 128 的数,用来存上机成绩 */
	unsigned c : 1;		/* 位字段,1 位二进制位,0 表示笔试不合格,1 表示笔试合格 */
	unsigned d : 1;		/* 位字段,1 位二进制位,0 表示上机不合格,1 表示上机合格 */
	unsigned e : 1;		/* 位字段,1 位二进制位,0 表示总评不合格,1 表示总评合格*/
};
void main () {
	int i;
	long j=2013000;
	int x[2] = {85, 50};			/* 假设两个人的笔试成绩 */
	int y[2] = {73, 65};			/* 假设两个人的上机成绩 */
	struct data g[2];				/* 定义结构体类型数组,分别存两个人的考试信息 */
	for(i = 0; i < 2; i++) {
		g[i].num = j + i + 1;		/* 往结构体数组 g 中生成学号、笔试成绩、上机成绩 */
		g[i].a = x[i];
		g[i].b = y[i];
		g[i].c = (g[i].a >= 60) ? 1 : 0;		/* 判断笔试成绩是否合格 */
		g[i].d = (g[i].b >= 60) ? 1 : 0;	/* 判断上机成绩是否合格 */
		g[i].e = ((g[i].c == 1) && (g[i].d == 1)) ? 1 : 0;		/* 判断总评成绩是否合格 */
		printf("number = %ld, a = %d, b = %d, c = %d, d = %d, e = %d \n",g[i].num,g[i].a,g[i].b,g[i].c,g[i].d,g[i].e);
	}
}

运行结果:

		number = 2013001, a = 85, b = 73, c = 1, d = 1, e = 1
		number = 2013002, a = 50, b = 65, c = 0, d = 1, e = 0

本例说明了位字段的赋值、运算及输出,与其他结构体或普通变量是一致的。
在计算机过程控制、参数检测和数据通信等领域中,要求应用程序具有对外部设备的控制和管理功能,而这些控制和管理是通过接口改变方式字或命令字和从接口读取状态字来实现的。这些命令字、方式字和状态字是以二进制位(bit)为单位的信息。所以,这种类型的数据经常需要 1 位或几位二进制位,定义位字段就足以解决问题。同时还节省空间,减少内存占用。

猜你喜欢

转载自blog.csdn.net/qq_33141353/article/details/88999908
9.3