C中的结构填充

Structures padding in C

int在我的机器上是4字节,long是8字节等

嘿,所以我在 C 中遇到了一件非常有趣的事情,并开始想知道结构如何在内部管理它们的数据。我以为它像数组一样工作,但是天哪,我错了。所以基本上,我认为里面的数据会自行总结,但我发现堆栈溢出时,由于处理器的架构要求,一些编译器可能会做一些优化。并且有对齐。我找到了两个关于对齐的链接,我想计算我的结构的大小并且我已经做了一些实验,但我想我在某些方面理解了这一点,而在某些方面则没有。这就是我想创建该主题的原因,因为我无法完全理解在这些主题中回答的人提供的一些示例。例如:

#include <stdio.h>

struct test {
    char a;
    char b;
    int c;
    long d;
    int e;
};

int main(void){
    printf("test = %d\n", sizeof(test));
    return 0;
}

输出:

test = 24

我原以为编译器会像这样进行优化: char a是1个字节,char b是1个字节,所以不需要对齐。 char b 是 1 个字节,int c 是 4 个字节,因此我们需要对齐 3 个字节。 int c是4个字节,long d是8个字节,所以我们需要对齐4个字节。 long d是8个字节,int e是4个字节,所以我们需要对齐4个字节。到目前为止,总大小为 29。用上限四舍五入到最接近的偶数得到 30。那为什么是 24?

我还发现 char a + char b 给出了一个等于 2 个字节的填充,所以我们只需要再对齐 2 个字节,因此也许这就是我犯错误的地方。另外,如果我添加更多变量:

#include <stdio.h>

struct test {
    char a;
    char b;
    int c;
    long d;
    int e;
    char f;
    char g;
    char h;
    char i;
};

int main(void){
    printf("test = %d\n", sizeof(test));
    return 0;
}

输出:

test = 24

总大小还是24字节。但是如果我再添加一个变量:

#include <stdio.h>

struct test {
    char a;
    char b;
    int c;
    long d;
    int e;
    char f;
    char g;
    char h;
    char i;
    char j;
};

int main(void){
    printf("test = %d\n", sizeof(test));
    return 0;
}

输出:

test = 32

大小更改为总共 32 个字节。为什么?到底发生了什么?抱歉,如果这个问题的答案对您来说很明显,但我真的不明白。另外我不知道编译器之间是否有所不同,所以如果我没有提供一些信息,请告诉我,我会添加。

这一切都归结为对齐。编译器希望使每个元素与一个地址对齐,该地址是该项目大小的倍数,因为硬件可以通过这种方式最有效地访问它。 (在某些架构上,硬件只能 以这种方式访问​​它);不允许未对齐的访问。)

您的结构中有一个 long int 大小为 8 的元素,因此它的对齐将驱动其他所有元素。这是您的第一个结构的布局方式:

   0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
 0 | a | b |  pad  |       c       |
   +---+---+---+---+---+---+---+---+
 8 |               d               |
   +---+---+---+---+---+---+---+---+
16 |       e       |     padding   |
   +---+---+---+---+---+---+---+---+

因此,如您所见,大小为 24,包括两个不可见的、未命名的“填充”字段,分别为 2 字节和 4 字节。

结构填充和对齐可能会造成混淆。 (令人尴尬的是,我尝试了很多次才得到正确的答案。)幸运的是,您通常不必担心这些,因为这是编译器的问题,而不是您的问题。

您可以使用 offsetof 宏让编译器告诉您它是如何布局结构的:

int main(void){
    printf("a @ %zd\n", offsetof(struct test, a));
    printf("b @ %zd\n", offsetof(struct test, b));
    printf("c @ %zd\n", offsetof(struct test, c));
    printf("d @ %zd\n", offsetof(struct test, d));
    printf("e @ %zd\n", offsetof(struct test, e));
    printf("size = %zd\n", sizeof(struct test));
    return 0;
}

在我的机器上(它的行为似乎与你的一样)打印:

a @ 0
b @ 1
c @ 4
d @ 8
e @ 16
size = 24

请注意,我使用 %zd 而不是 %d,因为 sizeofoffsetof 给出的答案类型为 size_t,而不是 [=21] =].

当您添加 char 个字段 fghi 时,它们可以放入第二个填充 space,而不会使整体结构变大。只有当您添加 j 时,它才会将内容推入另一个 8 字节块:

   0   1   2   3   4   5   6   7
   +---+---+---+---+---+---+---+---+
 0 | a | b |  pad  |       c       |
   +---+---+---+---+---+---+---+---+
 8 |               d               |
   +---+---+---+---+---+---+---+---+
16 |       e       | f | g | h | i |
   +---+---+---+---+---+---+---+---+
24 | j |          padding          |
   +---+---+---+---+---+---+---+---+