C语言中的位域

表示一个bool值,只要0和1两种状态,但是不得不使用char来表示,这会造成浪费。而位域就是可以让我们精细地控制位大小的方法。

所谓"位域"是把一个字节中的二进位划分为几个不同的区域,并说明每个区域的位数。每个域有一个域名,允许在程序中按域名进行操作。这样就可以把几个不同的对象用一个字节的二进制位域来表示。

位域的类型不可以是浮点型,可以是整型或字符型。它会像正常的类型一样参与运算,只是比特位不同,运算规则是相同的。

一个例子:

1
2
3
4
5
struct bs{
int a:8;
int b:2;
int c:6;
}data;

可以存在空域

1
2
3
4
5
6
struct bs{
unsigned a:4;
unsigned :4; /* 空域 */
unsigned b:4; /* 从下一单元开始存放 */
unsigned c:4
}
  • 位域的宽度不能超过它所依附的数据类型的长度,成员变量都是有类型的,这个类型限制了成员变量的最大长度,: 后面的数字不能超过这个长度。
  • 位域可以是无名位域,这时它只用来作填充或调整位置。无名的位域是不能使用的。

字节对齐

结构体大小与内部声明的成员变量类型和顺序有关,也与对齐方式有关。

C语言结构体字节对齐有几个准则:

  1. 各成员变量在类内起始地址偏移必须是成员大小的整数倍。因为访问数据的地址要满足一定的条件,能被这个数据的长度所整除。 例如,1字节数据必定是对齐的,2字节的数据的偏移要被2整除,4字节的数据地址要被4整除。

  2. 这个类的总大小必须是最大成员的整数倍。(或者可以说:struct末尾如果紧贴着一个相同类型的struct,必须满足够使下一个struct内成员对齐)

强制对齐可以使结构体按照指定大小对齐,使用#pragma pack(n)指定对齐大小;使用#pragma pop恢复对齐状态。

未对齐的数据会导致CPU:

  1. 产生一个异常条件
  2. 执行多次对齐访问,以便读取完整的未对齐数据值。(因为地址存取粒度不是单字节,而是双字节、四字节等)

位域的对齐

  • 如果相邻位域字段的类型相同,且其位宽之和小于类型的 sizeof 大小,则后面的字段将紧邻前一个字段存储,直到不能容纳为止;
  • 如果相邻位域字段的类型相同,但其位宽之和大于类型的 sizeof 大小,则后面的字段将从新的存储单元开始,其偏移量为其类型大小的整数倍;
  • 如果相邻的位域字段的类型不同,后面字段从下一个单元开始。
  • 整个结构体的总大小为最宽基本类型成员大小的整数倍。

参考

作者

limil

发布于

2024-09-25

更新于

2025-04-05

许可协议