为什么不管成员的顺序如何,这个结构的大小都是一样的?

Why the size of this structure is same irrespective of the order of the members?

由于 C 中结构的内存对齐是以第一个元素的连续形式完成的,然后是第二个元素,然后是第三个元素,依此类推...以及位填充,那么为什么这个结构的大小是相同的即使重新排列元素:

#include <stdio.h>

int main(void)
{
    struct student
    {
        float c;
        int a;
        char b;
    };
    printf("%zu\n", sizeof(struct student));
    return 0;
}

输出: 12

对于上面的结构配置,内存对齐看起来像这样吗?:

f f f f i i i i c
_ _ _ _ _ _ _ _ _ _ _
0 1 2 3 4 5 6 7 8 9 10

在您的系统上,intfloat 数据类型似乎各有 4 个字节;因此,编译器(除非另有说明)将在 4 字节边界上对齐这些类型的结构成员。这就是为什么,如果您将 char b 字段作为 second 成员,那么将在该字段和下一个字段之间添加 3 个字节的 'padding' - 给出结构的总大小为 12 个字节。

但是,编译器也会在结构的末尾添加填充!(三个字节,同样,在 char b 是最后一个字段的情况下。 )

为什么? 好吧,考虑这样的 struct 类型的数组。如果没有 'terminal padding',第二个数组元素的第一个字段将错位——也就是说,它将 而不是 在 4 字节边界上,从而降低从中获得的任何效率'internal' 填充。将您的类型作为嵌套字段包含在内的其他结构也会出现类似问题。

编辑:我无法对 this Wikipedia page 的以下陈述提供太多改进:

It is important to note that the last member is padded with the number of bytes required so that the total size of the structure should be a multiple of the largest alignment of any structure member …

看来在您的机器上 intfloat 都需要 4 个字节。从这个意义上说,它们在 struct 中的位置无关紧要。

但是,char(通常)只占用 1 个字节,因此您可能想知道 sizeof 为什么 return 9(4+4+ 1) 原因是 padding.

填充在许多情况下被添加,但最明显的是允许类型对齐(我假设你系统上的 intfloat 类型在 4 字节上自然对齐边界)。

我认为如果顺序更改为:

struct student
{
    float c;
    char b;
    int a;
};

在此示例中,我们将有 4 个字节 (float) + 1 个字节 (char) + 3 个字节(填充)+ 4 个字节 (int)。即:

struct student
{
    float c;
    char b;
    char padding[3];
    int a;
};

但是,在您的原始示例中,我们有:

struct student
{
    float c;
    int a;
    char b;
};

这导致 4 个字节 (float) + 4 个字节 (int) + 1 个字节 (char) + 3 个字节(填充)- 即:

struct student
{
    float c;
    int a;
    char b;
    char padding[3];
};

我们仍然在 struct 末尾进行填充的原因是允许数组 (struct student array[32])。

如果 struct 末尾没有任何填充,则数组的第二个成员 (array[1]) 将从偏移量和类型 (float ) 不会在自然的 4 字节边界上正确对齐。

当您声明类型时,编译器将始终添加允许在数组中使用该类型所需的填充(即,当使用 malloc 分配内存时)。

我希望这能回答你的问题。


编辑:

为了澄清我上面没有列出的剩余 struct 中的填充(见评论),它可能看起来像这样(假设编译器正在为类似系统编译代码):

struct student
{
    char b;
    char padding[3];
    float c;
    int a;
};

如果 a 也是 char,我们将在 struct 的两端填充:

struct student
{
    char b;
    char padding[3];
    float c;
    char a;
    char padding2[3];
};

但是,如果我们重新组织 struct 使字符彼此相邻,它们的填充将不同,因为 char 类型没有 4 字节的自然对齐在这个系统上:

struct student
{
    char b;
    char a;
    char padding[2];
    float c;
};

:

大多数编译器应该支持告诉编译器“打包”结构(忽略类型对齐和填充)的指令......但是,恕我直言,应该非常不鼓励这样做,因为它可能会导致一些 CPU体系结构崩溃并引入不可移植的代码 (see also here)。

编译器对齐字段并填充 struct 的剩余部分,因此如果您构建数组,下一个元素将对齐。

在您的 posted 案例中,字段元素的对齐方式是两个字段 floatint 的大小,这两个字段大 4 个字节。因此,编译器在末尾填充结构,以便您定义的类型的下一个数组元素也对齐。这意味着编译器必须在 char 类型字段后添加三个填充元素,即使它位于结构的末尾。

在你 post 的情况下,如果你认为结构是 5 个字节,结构数组的下一个元素将不会对齐,因为 float 和 int 将从 +1对齐偏移量。