C 知识量:16 - 74 - 317
操控位的第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中的顺序取决于机器。在有些机器上,存储的顺序是从左往右,而在另一些机器上,是从右往左。另外,不同的机器中两个字段边界的位置也有区别。
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;
Copyright © 2017-Now pnotes.cn. All Rights Reserved.
编程学习笔记 保留所有权利
MARK:3.0.0.20240214.P35
From 2017.2.6