首页 文章

填充C中的结构

提问于
浏览
40

这是一个面试问题 . 直到现在,我曾经认为这些问题纯粹依赖于编译器,不应该担心我,但现在,我对它很好奇 .

假设您有两种结构:

struct A {  
  int* a;  
  char b;  
 }

而且,

struct B {  
  char a;  
  int* b;  
}

那么你更喜欢哪一个?为什么?我的回答是这样的(虽然我有点在黑暗中拍摄)第一个结构应该是首选的,因为编译器为字大小的一些倍数(这是指针的大小 - 在32上的4个字节)分配结构的空间位机和64位的8字节) . 因此,对于这两种结构,编译器将分配8个字节(假设它是32位机器) . 但是,在第一种情况下,填充将在我的所有变量之后完成(即在a和b之后) . 因此,即使有一些机会,b得到一些溢出的值并破坏我的下一个填充字节,但我的a仍然是安全的 .

他似乎并不高兴,并要求第一个结构在第二个结构上的一个缺点 . 我没有太多话要说 . :d

请帮我解答 .

5 回答

  • 4

    我不认为任何这种结构都有优势 . 这个等式中有一个(!)常数 . 结构成员的顺序保证是声明的 .

    所以如下所示,第二个结构 might 有一个优势,因为它可能有一个较小的尺寸,但不是在你的例子中,因为它们可能具有相同的大小:

    struct {
        char a;
        int b;
        char c;
    } X;
    

    比 .

    struct {
        char a;
        char b;
        int c;
    } Y;
    

    A little more explanation regarding comments below:

    以下所有不是100%,但结构将在32位系统中构造的常见方式,其中int为32位:

    结构X:

    |     |     |     |     |     |     |     |     |     |     |     |     |
     char  pad    pad   pad   ---------int---------- char   pad   pad   pad   = 12 bytes
    

    结构Y:

    |     |     |     |     |     |     |     |     |
     char  char  pad   pad   ---------int----------        = 8 bytes
    
  • 2

    有些机器access data more efficiently当值与某些边界对齐时 . 一些require数据要对齐 .

    在现代32位机器上,如SPARC或英特尔[34] 86,或任何来自68020的摩托罗拉芯片,每个数据通常必须是“自对齐”,从一个地址的倍数开始它的类型大小 . 因此,32位类型必须从32位边界开始,16位边界上的16位类型,8位类型可以从任何地方开始,struct / array / union类型具有其最严格的成员的对齐 .

    所以你可以拥有

    struct B {  
        char a;
        /* 3 bytes of padding ? More ? */
        int* b;
    }
    

    在“自对齐”情况下最小化填充(并且在大多数其他情况下没有损害)的简单规则是通过减小大小来对结构成员进行排序 .

    我个人认为第一个结构与第二个结构相比并不缺点 .

  • 33

    在这种特殊情况下,我无法想到第一个结构在第二个结构上的缺点,但是有可能提出一些例子,其中将最大成员放在第一位的一般规则存在缺点:

    struct A {  
        int* a;
        short b;
        A(short num) : b(2*num+1), a(new int[b]) {} 
        // OOPS, `b` is used uninitialized, and a good compiler will warn. 
        // The only way to get `b` initialized before `a` is to declare 
        // it first in the class, or of course we could repeat `2*num+1`.
    }
    

    我还听说过大型结构的相当复杂的情况,其中CPU具有用于访问指针偏移的快速寻址模式,用于较小的偏移值(例如,高达8位,或立即值的某些其他限制) . 您最好通过在最快的指令范围内放置尽可能多的最常用字段来对大型结构进行微观优化 .

    CPU甚至可能具有指针偏移和指针4 *偏移的快速寻址 . 然后假设您有64个char字段和64个int字段:如果先放置char字段,那么可以使用最佳指令来处理这两种类型的所有字段,而如果先将int字段放入,则将不是4的字段放入-aligned只需要以不同方式访问,可能是通过将常量加载到寄存器而不是立即值,因为它们超出了256字节的限制 .

    从来没有必须自己做,例如x86无论如何都允许大的直接值 . 除非他们花费大量时间盯着装配,否则这不是任何人通常会想到的那种优化 .

  • 1

    简而言之,在一般情况下选择任何一个都没有优势 . 选择在实践中最重要的唯一情况是,如果启用了结构打包,则 struct A 将是更好的选择(因为两个字段将在内存中对齐,而在 struct Bb 字段将位于奇数偏移处) . 结构打包意味着在结构内部不插入填充字节 .

    但是,这是一种相当罕见的情况:结构打包通常仅在特定情况下启用 . 这不是大多数计划的关注点 . 并且它也不能通过C标准中的任何便携式结构来控制 .

  • 11

    这是也有一些猜测,但大多数编译器都有一个未对齐选项,明确不会添加填充字节 . 然后,这需要(在某些平台上)运行时修复(硬件陷阱)来动态地对齐访问(具有相应的性能损失) . 如果我记得正确HPUX属于这一类 . 因此,即使使用了错位编译器选项,第一个结构字段仍然是对齐的(因为正如您所说的填充将在最后) .

相关问题