【问题标题】:Size of structure with a char, a double, an int and a t [duplicate]带有 char、double、int 和 t 的结构的大小 [重复]
【发布时间】:2011-03-30 01:09:23
【问题描述】:

当我只运行代码片段时

int *t;
std::cout << sizeof(char)   << std::endl;
std::cout << sizeof(double) << std::endl;
std::cout << sizeof(int)    << std::endl;
std::cout << sizeof(t)      << std::endl;

它给了我这样的结果:

1
8
4
4

总计:17 个。

但是当我测试包含这些数据类型的 sizeof 结构时,它给了我 24,我很困惑。额外的 7 个字节是什么?

这是代码

#include <iostream>
#include <stdio.h>
struct struct_type{
    int i;
    char ch;
    int *p;
    double d;
} s;

int main(){
    int *t;
    //std::cout << sizeof(char)   <<std::endl;
    //std::cout << sizeof(double) <<std::endl;
    //std::cout << sizeof(int)    <<std::endl;
    //std::cout << sizeof(t)      <<std::endl;

    printf("s_type is %d byes long",sizeof(struct struct_type));

    return 0;
}

:编辑

我已经像这样更新了我的代码

#include <iostream>
#include <stdio.h>
struct struct_type{
    double d_attribute;
    int i__attribute__(int(packed));
    int * p__attribute_(int(packed));;
    char  ch;
} s;

int main(){
    int *t;
    //std::cout<<sizeof(char)<<std::endl;
    //std::cout<<sizeof(double)<<std::endl;
    //std::cout<<sizeof(int)<<std::endl;
    //std::cout<<sizeof(t)<<std::endl;

    printf("s_type is %d bytes long",sizeof(s));

    return 0;
}

现在它显示了 16 个字节。是不是很好,还是我丢失了一些重要的字节?

【问题讨论】:

    标签: c++ structure-packing


    【解决方案1】:

    comp.lang.c FAQ list · Question 2.12

    为什么我的编译器会在结构中留下漏洞、浪费空间并阻止“二进制”I/O 到外部数据文件?我可以关闭它,或者以其他方式控制结构字段的对齐方式吗?

    【讨论】:

      【解决方案2】:

      ...它给了我 24,我很困惑。额外的 7 个字节是什么?

      这些是编译器插入的填充字节。数据结构填充取决于实现。

      来自维基百科,Data structure alignment

      数据对齐意味着将数据放置在等于字大小的某个倍数的内存偏移处,由于 CPU 处理内存的方式,这提高了系统的性能。为了align数据,可能需要在最后一个数据结构的结尾和下一个数据结构的开头之间插入一些无意义的字节,这就是数据结构填充。

      【讨论】:

      • 是的。但是当我假设alignment=4 时,我希望是20,而不是24。
      • @Henk: double 在 Windows x86 上对齐 = 8。
      • @Henk - 当 double 有 8 个对齐时,这并不意味着其他字段有 8 个对齐 - 尽管整个结构都可以。
      • 在 Windows (Visual C++) 上,您可以使用 #pragma pack(1) 关闭填充。执行此操作时请务必使用#pragma pack(push/pop)。
      • +1 表示速度和正确性。
      【解决方案3】:

      为了稍微扩展 KennyDM 的出色答案(Kenny - 如果你愿意,请偷这个来补充你的答案),这可能是编译器对齐所有变量后你的内存结构的样子:

        0    1    2    3    4    5    6    7
      +-------------------+----+-----------+
      | i                 | ch | (unused)  |
      +-------------------+----+-----------+
      
        8    9   10   11   12   13   14   15
      +-------------------+----------------+
      | p                 |   (unused)     |
      +-------------------+----------------+
      
       16   17   18   19   20   21   22   23
      +------------------------------------+
      | d                                  |
      +------------------------------------+
      

      因此,由于“ch”和“p”之间的 3 字节间隙以及“p”和“d”之间的 4 字节间隙,您的结构得到了 7 字节的填充,因此大小为 24字节。由于您的环境的 double 具有 8 字节对齐(即,它必须驻留在它自己的 8 字节块中,如上所示),整个 struct 也将总体上是 8 字节对齐的,所以即使重新排序变量也不会改变 24 字节的大小。

      【讨论】:

      • 由于 double 有 8 字节对齐(否则结构将是 20 字节!),即使在重新排列之后,ch 之后也会有 7 字节的填充。
      • 啊,所以double 8 字节对齐会导致整个struct 这样做?我不知道,谢谢!
      • 是的,一般来说,整个结构必须与“最对齐”成员具有相同的对齐方式。想象一下,如果您有一个由这些结构组成的数组,则要求它们中的每一个都正确对齐其 double 成员,这只有在结构与 double 成员具有相同的对齐方式时才有可能。
      【解决方案4】:

      $9.2/12 声明 - “在没有中间访问说明符的情况下声明的(非联合)类的非静态数据成员被分配,以便后面的成员在类对象中具有更高的地址。非静态数据成员的分配顺序分开通过访问说明符未指定(11.1)。实现对齐要求 可能会导致两个相邻的成员不会被立即分配;所以可能 管理虚拟功能 (10.3) 和虚拟基类 (10.1) 的空间要求。”

      所以就像 sizeof(double) 和 sizeof(int) 一样,结构成员对齐的偏移量是未指定的,除了后面声明的成员位于更高的地址。

      【讨论】:

        【解决方案5】:

        由于填充,它是 24 个字节。 大多数编译器将数据填充到其大小的倍数。 因此,一个 4 字节的 int 被填充为 4 字节的倍数。 一个 8 字节的 double 被填充为 8 字节的倍数。 对于您的结构,这意味着:

        struct struct_type{
          int i; // offset 0 (0*4)
          char ch; // offset 4 (4*1)
          char padding1[3];
          int *p; // offset 8 (2*4)
          char padding1[4];
          double d; // offset 16 (2*8)
        }s;
        

        你可以像这样优化你的结构:

        struct struct_type{
          double d;
          int i;
          int *p;
          char ch;
        }s;
        

        sizeof(s)==17 在大多数编译器上(在其他一些编译器上为 20)

        【讨论】:

        • 即使重新排序后 sizeof 仍应为 24,因为 double 具有 8 字节对齐(而不是 4)。
        【解决方案6】:

        keep the alignments correct 的某些成员之间存在一些未使用的字节。例如,为了效率,一个指针默认驻留在 4 字节边界上,即它的地址必须是 4 的倍数。如果结构只包含一个字符和一个指针

        struct {
          char a;
          void* b;
        };
        

        那么b 不能使用#1 加法器——它必须放在#4。

          0   1   2   3   4   5   6   7
        +---+- - - - - -+---------------+
        | a | (unused)  | b             |
        +---+- - - - - -+---------------+
        

        在您的情况下,由于int* 的对齐,额外的 7 个字节来自 3 个字节,以及由于double 的对齐而导致的 4 个字节。

          0   1   2   3   4   5   6   7   8   9   a   b   c   d   e   f
        +---------------+---+- - - - - -+---------------+- - - - - - - -+
        | i             |ch |           | p             |               |
        +---------------+---+- - - - - -+---------------+- - - - - - - -+
         10  11  12  13  14  15  16  17
        +-------------------------------+
        | d                             |
        +-------------------------------+
        

        【讨论】:

        • +1 表示图表。
        • ... XD。只花了大约 10 分钟为他的记忆结构输入完整的表示;如果您认为它会进一步回答您的答案,甚至包括向您自己提出窃取它的提议,我发布它是为了发现您已经添加了自己的答案。呃,好吧。 XD。
        • +1 以获得非常好的答案。你的回答值得被接受。 :)
        • +1,我要补充一点,在使用 packed 属性之前,如果允许,它将关闭对齐(这意味着失去效率),简单地重新组织数据以最小化填充。在这种情况下,由于double 的严格要求,订单并不重要。
        • 非常好的答案...,带有图表表示。值得被接受。
        【解决方案7】:

        有时您还需要结构来维护所需的顺序。在这种情况下,如果您使用 gcc,则应使用 __attribute__((packed)) 语句。

        See also this 了解更多信息。

        【讨论】:

        • 在 C(或 gcc)中是否有任何方法可以指定特定数据项可能未对齐?一些处理器根本不支持直接访问未对齐的数据,因此 32 位读取或写入必须拆分为字节操作和移位。如果应用于每个 32 位指针取消引用,这样做的代码将是浪费的,但是能够指定必须使用此类代码取消引用某些指针会很有帮助。
        • @supercat:你为什么不简单地使用 memcpy? memcpy ((void *)&amp;place, (const void *)&amp;my_word, sizeof(my_word));
        【解决方案8】:

        额外的大小来自数据对齐,即成员对齐为 4 或 8 字节的倍数。

        您的编译器可能会将 int 和指向倍数的指针对齐 4 个字节,将双精度对齐到倍数的 8 个字节。

        如果您将双精度数移动到结构中的不同位置,您可能能够将结构的大小从 24 字节减少到 20 字节。但这取决于编译器。

        【讨论】:

          【解决方案9】:

          允许编译器将结构的成员与地址对齐,以便更快地访问。例如32 位边界。只有标准要求对象的成员按照声明的顺序存储。因此,当您需要内存中的确切位置时,请务必使用sizeofoffsetof

          【讨论】:

            猜你喜欢
            • 2013-09-25
            • 2018-10-08
            • 1970-01-01
            • 2021-10-11
            • 1970-01-01
            • 2011-02-23
            • 1970-01-01
            • 2010-12-22
            • 1970-01-01
            相关资源
            最近更新 更多