[英]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上使用以下编译器观察到的:
我也注意到clang的struct dumper有一些奇怪的地方。 如果我们要求不带尾部填充的数据大小(“ dsize”),
A B
first 8 9
second 7 8
然后在第一个示例中,我们得到dsize(A) == 8
。 为什么不是7?
我不是C ++的真正语言律师,但是到目前为止,我发现的是:
参考此问题的答案,结构仅保留为标准布局POD,而在其自身及其父类中只有1个类具有非静态成员。 因此,在这种情况下, A
在两种情况下都有保证的布局,而B
在两种情况下都没有保证。
支持这一点的事实是std :: is_pod对A
都是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 12
和8 8
。
原因很明确: A
可以用作某些类B
,然后调用func(b, a);
必须注意不要打扰可能位于填充区域中的b
其他成员(在OP的示例中为b.x8
);
我看不到C ++标准中A : X
任何特定属性,这会使g ++决定填充可以在struct A : X
重复使用,但不能在struct A
重复使用。 A
和A : X
都是可复制的 , 标准布局和POD 。
我想这只能是基于典型用法的优化决策。 不重复使用的版本将更快地复制。 也许g ++ ABI设计师可以发表评论?
有趣的是,此示例显示了可微复制的内容并不意味着memcpy(&b, &a, sizeof b)
等于b = a
!
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.