问题描述
由于 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
解决方法
在您的机器上,int
和 float
似乎各需要 4 个字节。从这个意义上说,它们在 struct
中的位置是无关紧要的。
然而,char
(通常)只占用 1 个字节,所以你可能想知道为什么 sizeof
不返回 9
(4+4+1) 原因是填充。
在许多情况下添加填充,但最明显的是允许类型对齐(我假设系统上的 int
和 float
类型在 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)。
,在您的系统上,int
和 float
数据类型似乎各有 4 个字节的大小;因此,编译器(除非另有说明)将在 4 字节边界上对齐这些类型的结构成员。这就是为什么,如果您将 char b
字段作为 second 成员,那么将在该字段和下一个字段之间添加 3 个字节的“填充” - 总大小为 12 个字节为结构。
但是,编译器也会在结构的末尾添加填充!(在 char b
是最后一个字段的情况下,同样是三个字节。)
为什么?好吧,考虑这样一个 struct
类型的数组。如果没有那个“终端填充”,第二个数组元素的第一个字段就会错位——也就是说,它不会位于 4 字节边界上,从而降低了从“内部”填充中获得的任何效率.对于将您的类型作为嵌套字段包含在内的其他结构,也会出现类似问题。
编辑:我真的不能对 this Wikipedia page 的以下声明提供太多改进:
需要注意的是最后一个成员用数字填充 所需的字节数,以便结构的总大小应为 任何结构成员的最大对齐的倍数……
,编译器对齐字段并填充 struct
的其余部分,因此如果您构建一个数组,下一个元素将被对齐。
在您发布的案例中,字段元素的对齐方式是 4 个字节大的两个字段 float
和 int
的大小。因此,编译器在末尾填充结构,以便您定义的类型的下一个数组元素也对齐。这意味着编译器必须在 char
类型字段之后添加三个填充元素,即使它位于结构的末尾。
在您发布的情况下,如果您认为结构是 5 个字节,则结构数组的下一个元素将不会对齐,因为 float 和 int 将从 +1
对齐的偏移量开始。