结构体的大小

问题描述 投票:0回答:3

据我所知,结构的大小取决于使用的编译器,并且编译器可能会添加填充以满足对齐要求。

在 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字节?

c struct alignment padding sizeof
3个回答
6
投票

编译器尝试对齐结构类型的对象,以使具有最严格对齐的数据成员适当对齐。

在此结构声明中

struct
{
    uint8 a;
    uint16 b;
    uint8 c;
}ABC;

对齐最严格的数据成员是数据成员

b
。其地址应按两个字节对齐。因此数据成员
a
被填充了一个字节。为了使该结构的对象的地址按 2 个字节对齐,数据成员
c
也用一个字节填充。


1
投票

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 和一些对齐要求 a
  • 编译器首先将大小 S 设置为零,并将对齐要求 A 设置为 1(字节)。
  • 编译器按顺序处理结构体中的每个成员:
  1. 考虑成员的s对齐要求a。如果 S 当前不是 a 的倍数,则向 S 添加足够的字节,使其成为 a 的倍数。这决定了该成员将去往何处;它将在距结构开头的偏移量 S 处(对于 S 的当前值)。
  2. A设置为Aa的最小公倍数1
  3. s添加到S,为成员留出空间。
  • 对每个成员完成上述过程后,请考虑结构的s对齐要求A。如果 S 当前不是 A 的倍数,则添加足够多的 S,使其成为 A 的倍数。

完成上述操作后,结构体的大小就是S的值。

另外:

  • 如果任意成员是数组,则其大小为元素个数乘以每个元素的大小,其对齐要求为某个元素的对齐要求。
  • 如果任何构件是结构,其尺寸和对齐要求按上述计算。
  • 如果任何成员是联合,则其大小为其最大成员的大小加上足以使其成为所有成员对齐的最小公倍数1的倍数。

对于基本类型(

int
double
等),对齐要求是实现定义的,通常很大程度上由硬件决定。在许多处理器上,当数据具有一定的对齐方式时(通常当其在内存中的地址是其大小的倍数时),加载和存储数据会更快。除此之外,上述规则很大程度上遵循逻辑;他们将每个成员放置在必须满足对齐要求的位置,而不会使用不必要的空间。

脚注

1 我在一般情况下将其表述为使用对齐要求的最小公倍数。然而,由于对齐要求始终是 2 的幂,因此任何一组对齐要求的最小公倍数是其中最大的一个。


0
投票

编译器可能会添加填充以满足对齐要求。请注意,这不仅适用于结构体字段之间的填充,还可能适用于结构体的末尾(以便结构体类型的数组将使每个元素正确对齐)。

例如:

struct foo_t {
    int x;
    char c;
};

即使 c 字段不需要填充,结构体通常会有 sizeof(struct foo_t) == 8 (在 32 位系统上 - 而不是具有 32 位 int 类型的系统),因为需要是 c 字段之后的 3 个字节的填充。

请注意,系统(如 x86 或 Cortex M3)可能不需要填充,但出于性能原因,编译器可能仍会添加它。

© www.soinside.com 2019 - 2024. All rights reserved.