【问题标题】:Size of struct containing double field包含双字段的结构的大小
【发布时间】:2012-08-08 00:58:06
【问题描述】:

首先,我了解结构中的字节填充。但是我仍然有一个小测试在结构中包含一个双字段,我不知道如何解释:

typedef struct {
    char a;
    double b;
}data;

typedef struct{
    char a;
    int b;
}single;

int main(){
    printf("%d\n",sizeof(double));
    printf("%d\n",sizeof(single));
    printf("%d\n",sizeof(data));
}

通过这次测试,答案是:8816

为什么这个结果让我思考?

通过第二次测试,我们可以看到我机器上的字大小是 4 个字节。

通过第一次测试,我们可以看到 double 的大小是 8 个字节。

所以,在结构 data 处:结果应该是 12 个字节:char 为 4 个字节,double 为 8 个字节。

但是,我不知道为什么结果是 16 个字节。 (对我来说很奇怪)

请给我解释一下,谢谢:)

【问题讨论】:

    标签: c struct sizeof


    【解决方案1】:

    通常用于在结构中布置数据的过程基本上是这样的:

    • 设置偏移量 = 0。
    • 对于结构中的每个成员:假设 A 为其对齐要求(例如,1、2、4 或 8 个字节,可能更多)。将使其成为 A 的倍数所需的字节数添加到 Offset。(假设 A 是 2 的幂,这可以使用 Offset += -Offset & A-1 来完成,假设否定的二进制补码。)将 Offset 的当前值分配给成为该成员的偏移量。将成员的大小添加到 Offset。
    • 处理完所有成员后:令 A 为任何成员的最大对齐要求。将需要的字节数添加到 Offset 以使其成为 A 的倍数。Offset 的最终值是结构的大小。

    正如 Earnest Friedman-Hill 所说,最后一步将填充添加到结构的末尾,以便在它们的数组中,每个结构都以所需的对齐方式开始。

    因此,对于像struct { char c; double d; int32_t i; } 这样的结构,在典型实现中,您有:

    • 将偏移设置为 0。
    • char 需要对齐 1,因此 Offset 已经是 1 的倍数(0•1 为 0)。将 c 放在这个偏移量 0 处。将 c 的大小 1 添加到 Offset 中,使其为 1。
    • double 需要对齐 8,因此将 7 添加到 Offset,使其成为 8。将 d 放在此偏移处,8。将 d 的大小,8,添加到 Offset,使其成为 16。
    • int 需要对齐 4,因此 Offset 已经是 4 的倍数(4•4 是 16)。将 i 放在这个偏移量 16 处。将 i 的大小 4 添加到 Offset 中,使其变为 20。
    • 最后,需要的最大对齐是 8,所以在 Offset 上加 4,变成 24。这个结构的大小是 24 字节。

    请注意,以上内容与机器的任何字长无关。它只使用每个成员的对齐要求。每种类型的对齐要求可以不同,也可以根据类型的大小不同,上述方法仍然有效。

    (如果对齐要求不是 2 的幂,则算法会中断。这可以通过使最后一步将偏移量增加到所有对齐的最小公倍数的倍数来解决。)

    【讨论】:

    • 哇。惊人的 !!!你的帖子不错。这也让我理解了之前的其他帖子。 :) 最后一句话,你能告诉我更多关于最后一步的信息吗:Add to Offset the number of bytes needed to make it a multiple of A。为什么它会使处理数据更有效?谢谢:)
    • 不是为了“更有效地处理数据”。这是为了满足一个不能被打破的绝对约束。如果一个类型具有必需的对齐方式,则它永远不会存在,除非在该对齐方式处。在某些情况下,底层机器没有这样的限制,但 C 实现仍然出于任意原因做出这样的限制,例如支持确实有限制的其他机器变体,或者克服底层机器上未对齐数据的性能问题。
    • @hqt:当你创建一个结构体数组时,数组元素的间距等于结构体的大小。如果该大小不是最大对齐要求的倍数,则需要该对齐的成员将不在对齐位置。例如,如果示例结构的大小为 20 字节,那么第一个元素将位于偏移量 0 处,成员 (c, d, i) 位于偏移量 0、8 和 16 处,这很好。但是数组的第二个元素在偏移量 20 处,成员在偏移量 20、28 和 32 处。由于 28 不是 8 的倍数,因此成员 d 未对齐。
    【解决方案2】:

    你有什么奇怪的(或者我错过了什么)?

    逻辑是一样的(填充是根据struct中的“最大”primitive字段(我的意思是-int、double、char等))

    就像single,你有

    1 (sizeof(char)) + 3 (padding) + 4 (sizeof(int))
    

    data 也是一样的:

    1 (sizeof(char)) + 
    7 (padding, it's sizeof(double) - sizeof(char)) + 
    8 (sizeof(double))
    

    这是 16。

    【讨论】:

    • 对不起,我不认为字节填充是根据最大的字段。我认为它建立在 word 上:一个 CPU 单元。它总是会让一个词适合一个词。并且因为 word 是 4 个字节(我的机器),所以前四个字节用于 char(3 个字节填充),而 double 将适合两个字。 --> 总和:12
    • @hqt:机器字长和要求的对齐不一样。首先,一台机器可能有几个字长,就不同目的的数据大小而言。一台机器可能有 4 字节的通用寄存器,因此 32 位整数适用于算术运算,同时它具有 8 字节宽的总线,因此 64 位适用于加载和存储。其次,每种数据类型所需的对齐方式取决于该数据类型的加载和存储指令。这些加载和存储指令可能需要 4 字节对齐、8 字节对齐或其他内容。
    • @hqt - 没错,Eric 是对的。填充和对齐方式不一样。
    【解决方案3】:

    对齐取决于编译器,除非您使用编译器特定指令明确指定它。

    变量不一定是字对齐的。有时它们是双字对齐以提高效率。在浮点的特殊情况下,它们可以对齐到更高的值,以便 SSE 可以工作。

    【讨论】:

      【解决方案4】:

      编译器可能会将所有结构大小对齐为 8 的倍数

      【讨论】:

      • 请您再解释一下。我不太明白你的想法。谢谢:)
      • 欧内斯特解释得更好。编译器填充结构以提高性能。如果您有一个大小为 12 的对象数组。假设第一个对象为 0,第二个对象为 12 - 这不是 8 的倍数 - 这可能会减慢内存访问速度 - 这取决于他的平台(它可以生成 2 次单独的内存读取来获取该数字)。
      【解决方案5】:

      它是 16 个字节,因此如果您有一个 datas 数组,则 double 值都可以在 8 字节边界上对齐。在内存中正确对齐数据可以对性能产生很大影响。未对齐的数据操作起来可能会更慢,获取和存储的速度也会更慢。

      【讨论】:

      • 您可能还想在single 结构中添加它,由于int 的对齐,大小为8。 char 的大小可能为 1,但填充占其他三个字节。
      • 性能并不是对齐数据的唯一原因。在许多机器上,加载或存储到未对齐的地址会导致异常,并且许多操作系统不会模拟它们,因此您的程序会崩溃。
      • 你能给我解释更多关于术语align data的信息吗?谢谢:)
      • 基本上,对于原始类型,它通常存储为内存地址是其大小的倍数。在结构中,这有时意味着在这里和那里添加填充以保持不变。
      • (至少,我使用的架构存储了这样的变量。无论如何,字节填充用于确保变量存储在架构正在寻找的任何内容的倍数中. 因此,我们将数据“对齐”到那个倍数)
      猜你喜欢
      • 2012-09-27
      • 2016-11-21
      • 1970-01-01
      • 1970-01-01
      • 2011-11-22
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多