位域的最小尺寸

问题描述

我在理解 sizeof 返回的结果时遇到了一些问题。

假设我有两个结构:

typedef struct{
    unsigned int  age    : 4;
    unsigned char gender : 1;
    unsigned int  size   : 2;
} child_t;

typedef struct{
    unsigned int  age    : 4;
    unsigned char gender : 1;
    unsigned int  size   : 2;
} __attribute__((packed)) child_t_packed;

https://coliru.stacked-crooked.com/view?id=10866c66fe82f4e4

你能解释为什么第一个结构体的大小是 4 个字节吗?

可能我错了,但我期望 2 个字节。 (第一行4位,第二行3位)

打包版本忽略对齐,在第二种情况下sizeof(child_t_packed) = 1 byte. 这是否意味着 1 个字节是 child_t_packed 类型的变量在内存中可能占用的最小大小?或者最小值可以是 7 位?

解决方法

child_t 的大小为四个字节,因为实现决定将 structunsigned int 对齐,即 agesize 的类型.

类对象中位域的分配是实现定义的。位域的对齐是实现定义的。位域被打包到一些可寻址的分配单元中。

[class.bit]

如果 child_t 仅由 unsigned char 位字段成员组成,那么对齐将是 unsigned char 的对齐,而整个 struct 将(很可能,它仍然是实现-定义)适合单个字节。

typedef struct{
    unsigned char age     : 4;
    unsigned char gender  : 1;
    unsigned char size    : 2;
} child_t_char;

https://godbolt.org/z/MGKPqGPKq

child_t_packed 的大小为 1,因为我们使用 packed 属性强制执行最小填充。

packed 属性指定结构成员应具有尽可能小的对齐方式——位字段一位,否则一位字节,除非使用对齐属性指定了更大的值。该属性不适用于非成员对象。

GCC 11 documentation on variable attributes

由于 4+1+2 位适合单个字节,因此大小为 1。这意味着 child_t_packed 类型的对象将恰好占用内存中的一个字节。

,

在标准 C++ 中,结构体的最小可能大小是 1 char(至少 8 位)。因此,即使像 __attribute__((packed)) 这样的扩展也被迫产生大小为 1 的结构体。

此外,几乎所有关于位域布局的内容都是实现定义的。

在这个例子中,GCC 似乎只是为位域对齐选择了最大的指定成员类型 (int) 的对齐方式。

如果您将 int 更改为 short,那么 sizeof(child_t) 将产生 2

typedef struct{
    unsigned short age    : 4;
    unsigned char  gender : 1;
    unsigned short size   : 2;
} child_t;

https://coliru.stacked-crooked.com/a/3db0b400312e3d2c