C

C 知识量:16 - 74 - 317

15.3 位字段><

位字段简介- 15.3.1 -

操控位的第2种方法是位字段。位字段是一个signed int或unsigned int类型变量中的一组相邻的位。

位字段通过一个结构声明来建立,该结构声明为每个字段提供标签,并确定该字段的宽度。例如,下面的声明建立了一个4个1位的字段:

struct {
    unsigned int autfd : 1;
    unsigned int bldfc : 1;
    unsigned int undln : 1;
    unsigned int itals : 1;
} prnt;

prnt包含4个1位的字段。由于每个字段恰好为1位,所以只能为其赋值1或0。变量prnt被储存在int大小的内存单元中,但是在本例中只使用了其中的4位。

带有位字段的结构提供一种记录设置的方便途径。许多设置(如,字体的粗体或斜体)就是简单的二选一。例如,开或关、真或假。如果只需要使用1位,就不需要使用整个变量。内含位字段的结构允许在一个存储单元中储存多个设置。

有时,某些设置也有多个选择,因此需要多位来表示。字段不限制1位大小,可以使用如下的代码:

struct {
    unsigned int code1 : 2;
    unsigned int code2 : 2;
    unsigned int code3 : 8;
} prcode;

以上代码创建了两个2位的字段和一个8位的字段。注意:赋值时要确保所赋的值不超出字段可容纳的范围,例如可以这样赋值:

prcode.code1 = 0;
prcode.code2 = 3;
prcode.code3 = 102;

如果声明的总位数超过了一个unsigned int类型的大小,就会用到下一个unsigned int类型的存储位置。一个字段不允许跨越两个unsigned int之间的边界。编译器会自动移动跨界的字段,保持unsigned int的边界对齐。一旦发生这种情况,第1个unsigned int中会留下一个未命名的“洞”。

可以用未命名的字段宽度“填充”未命名的“洞”。使用一个宽度为0的未命名字段迫使下一个字段与下一个整数对齐:

struct {
    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中的顺序取决于机器。在有些机器上,存储的顺序是从左往右,而在另一些机器上,是从右往左。另外,不同的机器中两个字段边界的位置也有区别。

对齐特性- 15.3.2 -

C11的对齐特性比用位填充字节更自然,它们还代表了C在处理硬件相关问题上的能力。在这种上下文中,对齐指的是如何安排对象在内存中的位置。例如,为了效率最大化,系统可能要把一个double类型的值储存在4字节内存地址上,但却允许把char储存在任意地址。有些情况会受益于对齐控制,例如,把数据从一个硬件位置转移到另一个位置,或者调用指令同时操作多个数据项。

_Alignof运算符给出一个类型的对齐要求,在关键字_Alig-nof后面的圆括号中写上类型名即可,例如:

size_t d_align = _Alignof(float);

假设d_align的值是4,意思是float类型对象的对齐要求是4。也就是说,4是储存该类型值相邻地址的字节数。

一般而言,对齐值都应该是2的非负整数次幂。较大的对齐值被称为stricter或stronger,较小的对齐值被称为weaker。

可以使用_Alignas说明符指定一个变量或类型的对齐值。但是,不应该要求该值小于基本对齐值。例如,如果float类型的对齐要求是4,不要请求其对齐值是1或2。该说明符用作声明的一部分,说明符后面的圆括号内包含对齐值或类型:

_Alignas(double) char c1;
_Alignas(8) char c2;