问题描述
我在理解 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
的大小为四个字节,因为实现决定将 struct
与 unsigned int
对齐,即 age
和 size
的类型.
类对象中位域的分配是实现定义的。位域的对齐是实现定义的。位域被打包到一些可寻址的分配单元中。
如果 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;