为什么这个struct padding技巧有效?

时间:2015-07-07 00:15:36

标签: c++ inheritance padding

考虑这个简单的程序

#include <iostream>

struct A
{
    int   x1234;
    short x56;
    char  x7;
};

struct B : A
{
    char x8;
};

int main()
{
    std::cout << sizeof(A) << ' ' << sizeof(B) << '\n';
    return 0;
}

这会打印8 12。即使B可以打包成8个字节而不破坏对齐要求,但它占用了12个字节的贪心。

拥有sizeof(B) == 8会很高兴,但答案是 Is the size of a struct required to be an exact multiple of the alignment of that struct?表明没有办法。

以下

时我感到很惊讶
struct MakePackable
{
};

struct A : MakePackable
{
    int   x1234;
    short x56;
    char  x7;
};

struct B : A
{
    char x8;
};

打印8 8

这里发生了什么?我怀疑标准布局类型与它有关。如果是这样,那么当该特性的唯一目的是确保与C的二进制兼容时,导致上述行为的理由是什么?

编辑:正如其他人已经指出这是ABI或编译器特定的,所以我应该补充一点,在x86_64-unknown-linux-gnu上使用以下编译器观察到这种行为:

  • clang 3.6
  • gcc 5.1

我也注意到clang的结构转储器有些奇怪。如果我们要求数据大小没有尾部填充(&#34; dsize&#34;),

          A   B
first     8   9
second    7   8

然后在第一个例子中我们得到dsize(A) == 8。为什么这不是7?

2 个答案:

答案 0 :(得分:2)

我不是C ++的真正语言律师,但到目前为止我发现的是:

引用this question中的答案,结构只保留标准布局POD,而在自身及其父类中只有一个类具有非静态成员。因此,根据这个想法,A在两种情况下都有保证的布局,但{em}

支持这一点的事实是BA为真,而B为{}。

因此,如果我自己正确理解这一点,那么在两种情况下,编译器都可以通过B的布局获得一些空间。显然在第二种情况下,感觉就像使用A的填充字节一样。

答案 1 :(得分:2)

这是一个数据点,虽然不是一个完整的答案。

假设我们(作为完整的翻译单元,而不是代码段):

struct X {};

struct A
{
    int   x1234;
    short x56;
    char  x7;
}

void func(A &dst, A const &src) 
{
    dst = src;
}

使用g ++,此函数编译为:

movq    (%rdx), %rax
movq    %rax, (%rcx)

但如果使用struct A : X代替,则此函数为:

movl    (%rdx), %eax
movl    %eax, (%rcx)
movzwl  4(%rdx), %eax
movw    %ax, 4(%rcx)
movzbl  6(%rdx), %eax
movb    %al, 6(%rcx)

在OP的示例中,这两种情况实际上分别对应于8 128 8的尺寸。

原因很明显:A可能会被用作某个班级B的基础,然后调用func(b, a);必须小心,不要打扰其他成员{ {1}}可能位于填充区域(OP示例中为b);

我在C ++标准中看不到b.x8的任何特定属性,这会使g ++决定填充在A : X中可重复使用,而不是在struct A : X中。 struct AA都是平易可复制的标准布局 POD

我想这只是基于典型用法的优化决策。没有重复使用的版本将更快复制。也许g ++ ABI设计师可以发表评论?

有趣的是,这个例子表明,轻微可复制并不意味着A : X等同于memcpy(&b, &a, sizeof b)