位字段定义
位字段:一个int或者unsigned int类型变量中的一组相邻的位。
位字段通过一个结构声明来建立,该结构声明为每个字段提供标签,并确定该字段的宽度。不同于一般结构体的是它在定义成员的时候需要指定成员所占的位数。
位字段用途
带有位字段的结构提供一种记录设置的方便途径。许多设置(如,字体的粗体或者斜体)就是简单的二选一。例如,开或关、真或假。如果只需要使用1位,就不需要使用整个变量。内含位字段的结构允许在一个存储单元中储存多个设置。它主要用于一些使用空间很宝贵的程序设计中,如嵌入式程序设计。
使用单个位例子
例如,下面的结构体声明建立了一个 4个1位的字段。
//结构体PRNT的声明中,包含了4个1位的字段。
struct PRNT
{
unsigned int autfd : 1; //占用1位
unsigned int bldfc : 1; //占用1位
unsigned int undln : 1;
unsigned int itals : 1;
} obj;
//结构体赋值操作,现在可以通过普通的结构成员运算符(.)单独给这些字段赋值。
obj.itals = 0;
obj.undln = 1; //可以为0,可以为1,因声明了一位只有两种选择
由于每个字段恰好为1位,所以只能为其赋值1或0。变量obj被储存在int大小的内存单元中,但是以上结构体只使用了其中的4位。
使用多个位例子
有时,某些设置也有多个选择,因此需要多位来表示。字段不限制1位大小。可以使用如下方式:
//创建两个2位字段和一个8位字段的结构体
struct PRCODE
{
unsigned int code1 : 2; //支持二进制的四个数:00,01,10,11
unsigned int code2 : 2;
unsigned int code3 : 8;
}prcode;
//赋值方式
prcode.code1 = 0;
prcode.code1 = 3;
prcode.code1 = 102;
声明总位数超出问题
如果声明的总位数超过了一个unsigned int 类型的大小会怎样?
会用到下一个unsigned int类型的存储位置。一个字段不允许跨越两个unsigned int 之间的边界。编译器会自动移动跨界的字段,保持unsigned int的边界对齐。一旦发生这种情况,第1个unsigned int中会留下一个未命名的“洞”。
超出解决办法
用未命名的字段“填充”未命名的“洞”,使用一个宽度为0的未命名字段迫使下一个字段与下一个整数对齐:
struct STUFF
{
unsigned int field1 : 1;
unsigned int : 2; //未命名的字段
unsigned int field2 : 1;
unsigned int : 0; //未命名的字段
unsigned int field3 : 1;
}stuff;
这里,在stuff.field1和stuff.field2之间,有一个2位的空隙;stuff.field3将储存在下一个unsigned int中。
位字段弊端
字段储存在一个int中的顺序取决于机器。在有些机器上,存储的顺序是从左往右的,而另一些机器,是从右往左的。另外,不同的机器中两个字段边界的位置也有区别。使用位字段定义的数据不能在不同字节顺序的机器之间移动。由于这些原因,位字段通常都不容易移植。
对于位字段中的成员不能用位操作符进行运算,因为它们没有地址。
尽管如此,有些情况却要用到这种不可移植的特性。例如,以特定硬件设备所用的形式储存数据。
位字段示例
#include <stdio.h>
#define true 1
#define false 0
#define SOLID 0 //实线
#define DOTTED 1 //点线
#define DASHED 2 //虚线
//三原色
#define BLUE 4 //蓝
#define GREEN 2 //绿
#define RED 1 //红
//混合色
#define BLACK 0 //黑色
#define YELLOW (RED | GREEN) //黄色
#define MAGENTA (RED | BLUE) //洋红色
#define CYAN (GREEN | BLUE) //蓝绿色、青绿色
#define WHITE (RED | GREEN | BLUE) //白色
typedef unsigned int uint;
const char * colors[8] = {"black", "red", "green", "yellow", "blue", "magenta", "cyan", "white"};
typedef struct Box_Props
{
uint opaque : 1;
uint fill_color : 3;
uint : 4;
uint show_border : 1;
uint border_color : 3;
uint border_style : 2;
uint : 2;
} box_props;
void show_settings(const box_props * pb);
int main()
{
//插件并初始化 box_props结构
box_props box = {true, YELLOW, true, GREEN, DASHED};
printf("Original box setting ");
show_settings(&box);
box.opaque = false;
box.fill_color = WHITE;
box.border_color = MAGENTA;
box.border_style = SOLID;
printf("\nModified box settings:\n");
show_settings(&box);
return 0;
}
void show_settings(const box_props * pb)
{
printf("Box is %s.\n", pb->opaque == true ? "opaque" : "transpatent");
printf("The file color is %s.\n", colors[pb->fill_color]);
printf("Border %s.\n", pb->show_border == true ? "shown" : "not shown");
printf("The border color is %s.\n", colors[pb->border_color]);
printf("The border style is ");
switch(pb->border_style)
{
case SOLID:
printf ("solid.\n");
break;
case DOTTED:
printf ("dotted.\n");
break;
case DASHED:
printf ("dashed.\n");
break;
default:
printf ("unknown type.\n");
}
}
下面是该程序的输出:
Original box setting:
Box is opaque.
The file color is yellow.
Border shown.
The border color is green.
The border style is dashed.
Modified box settings:
Box is transpatent.
The file color is white.
Border shown.
The border color is magenta.
The border style is solid.
参考
- 位字段-百度百科
- c prime plus (第6版) 中文版 15.4 位字段
- C语言中的位字段 - caianye的专栏