据我所知,结构的大小取决于使用的编译器,并且编译器可能会添加填充以满足对齐要求。
在 64 位系统上。我测试了两个例子:
示例1:
struct
{
uint8 a;
uint32 b;
uint8 c;
}ABC;
size of(uint8 a) == 1 bytes + 3 bytes padding
size of(uint32 b) == 4 bytes + 0 padding
size of(uint8 c) == 1 bytes + 3 padding
==> So, size of(ABC) = 12 bytes.
示例2:
struct
{
uint8 a;
uint16 b;
uint8 c;
}ABC;
size of(uint8 a) == 1 bytes + 1 bytes padding
size of(uint16 b) == 2 bytes + 0 padding
size of(uint8 c) == 1 bytes + 3 padding
==> So, I assumed size of(ABC) = 8 bytes.
但是,编译器返回 (ABC) 的大小 = 6 字节。
为什么示例2中(ABC)的大小= 6字节而不是我理解的8字节?
编译器尝试对齐结构类型的对象,以使具有最严格对齐的数据成员适当对齐。
在此结构声明中
struct
{
uint8 a;
uint16 b;
uint8 c;
}ABC;
对齐最严格的数据成员是数据成员
b
。其地址应按两个字节对齐。因此数据成员 a
被填充了一个字节。为了使该结构的对象的地址按 2 个字节对齐,数据成员 c
也用一个字节填充。
size of(uint8 c) == 1 bytes + 3 padding`
==> So, I assumed size of(ABC) = 8 bytes.
没有理由添加三个字节的填充。因为
uint8
(大概是真的 uint8_t
或等效项)的对齐要求是一个字节,而 uint16
(大概真的是 uint16_t
或等效项)的对齐要求是两个字节,所以完整结构的对齐要求是其中最多两个字节。使用一个字节用于 uint8 a
、一个字节用于填充以使 uint16 b
对齐、两个字节用于 uint16 b
、一个字节用于 uint8 c
,到此为止的结构大小为 5 个字节。那么只需要多一个字节就可以使其成为其对齐要求的倍数,因此总共是六个字节。
通常用于布局结构的规则是:
完成上述操作后,结构体的大小就是S的值。
另外:
对于基本类型(
int
、double
等),对齐要求是实现定义的,通常很大程度上由硬件决定。在许多处理器上,当数据具有一定的对齐方式时(通常当其在内存中的地址是其大小的倍数时),加载和存储数据会更快。除此之外,上述规则很大程度上遵循逻辑;他们将每个成员放置在必须满足对齐要求的位置,而不会使用不必要的空间。
1 我在一般情况下将其表述为使用对齐要求的最小公倍数。然而,由于对齐要求始终是 2 的幂,因此任何一组对齐要求的最小公倍数是其中最大的一个。
编译器可能会添加填充以满足对齐要求。请注意,这不仅适用于结构体字段之间的填充,还可能适用于结构体的末尾(以便结构体类型的数组将使每个元素正确对齐)。
例如:
struct foo_t {
int x;
char c;
};
即使 c 字段不需要填充,结构体通常会有 sizeof(struct foo_t) == 8 (在 32 位系统上 - 而不是具有 32 位 int 类型的系统),因为需要是 c 字段之后的 3 个字节的填充。
请注意,系统(如 x86 或 Cortex M3)可能不需要填充,但出于性能原因,编译器可能仍会添加它。