位字段的声明格式为:
类型[成员名称]:宽度;
各部分的详细描述如下:
(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 ));
原创文章,作者:奋斗,如若转载,请注明出处:https://blog.ytso.com/21664.html