为什么无论成员的顺序如何,这个结构的大小都是相同的?

如何解决为什么无论成员的顺序如何,这个结构的大小都是相同的?

由于 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 个字节。从这个意义上说,它们在 struct 中的位置是无关紧要的。

然而,char(通常)只占用 1 个字节,所以你可能想知道为什么 sizeof 不返回 9 (4+4+1) 原因是填充

在许多情况下添加填充,但最明显的是允许类型对齐(我假设系统上的 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)。

,

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

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

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

编辑:我真的不能对 this Wikipedia page 的以下声明提供太多改进:

需要注意的是最后一个成员用数字填充 所需的字节数,以便结构的总大小应为 任何结构成员的最大对齐的倍数……

,

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

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

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

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?
Java在半透明框架/面板/组件上重新绘画。
Java“ Class.forName()”和“ Class.forName()。newInstance()”之间有什么区别?
在此环境中不提供编译器。也许是在JRE而不是JDK上运行?
Java用相同的方法在一个类中实现两个接口。哪种接口方法被覆盖?
Java 什么是Runtime.getRuntime()。totalMemory()和freeMemory()?
java.library.path中的java.lang.UnsatisfiedLinkError否*****。dll
JavaFX“位置是必需的。” 即使在同一包装中
Java 导入两个具有相同名称的类。怎么处理?
Java 是否应该在HttpServletResponse.getOutputStream()/。getWriter()上调用.close()?
Java RegEx元字符(。)和普通点?