繁体   English   中英

为什么此结构填充技巧起作用?

[英]Why does this struct padding trick work?

考虑这个简单的程序

#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会很好,但是答案是:结构的大小是否必须为该结构的对齐方式的精确倍数? 暗示没有办法。

因此,当以下情况使我感到惊讶

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上使用以下编译器观察到的:

  • 铛3.6
  • gcc 5.1

我也注意到clang的struct dumper有一些奇怪的地方。 如果我们要求不带尾部填充的数据大小(“ dsize”),

          A   B
first     8   9
second    7   8

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

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

参考此问题的答案,结构仅保留为标准布局POD,而在其自身及其父类中只有1个类具有非静态成员。 因此,在这种情况下, A在两种情况下都有保证的布局,而B两种情况下都没有保证。

支持这一点的事实是std :: is_podA都是true,而对于B都是false。

因此,如果我本人正确理解了这一点,则在两种情况下都允许编译器有一些空间来完成B的布局。 显然在第二种情况下,感觉就像是利用了A的填充字节。

尽管这不是一个完整的答案,但这是一个数据点。

说我们有(作为完整的翻译单位,而不是摘录):

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); 必须注意不要打扰可能位于填充区域中的b其他成员(在OP的示例中为b.x8 );

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

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

有趣的是,此示例显示了可微复制的内容并不意味着memcpy(&b, &a, sizeof b)等于b = a

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM