考虑这个简单的程序
#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的结构转储器有些奇怪。如果我们要求数据大小没有尾部填充(&#34; dsize&#34;),
A B
first 8 9
second 7 8
然后在第一个例子中我们得到dsize(A) == 8
。为什么这不是7?
答案 0 :(得分:2)
我不是C ++的真正语言律师,但到目前为止我发现的是:
引用this question中的答案,结构只保留标准布局POD,而在自身及其父类中只有一个类具有非静态成员。因此,根据这个想法,A
在两种情况下都有保证的布局,但{em}
支持这一点的事实是B
对A
为真,而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 12
和8 8
的尺寸。
原因很明显:A
可能会被用作某个班级B
的基础,然后调用func(b, a);
必须小心,不要打扰其他成员{ {1}}可能位于填充区域(OP示例中为b
);
我在C ++标准中看不到b.x8
的任何特定属性,这会使g ++决定填充在A : X
中可重复使用,而不是在struct A : X
中。 struct A
和A
都是平易可复制的,标准布局和 POD 。
我想这只是基于典型用法的优化决策。没有重复使用的版本将更快复制。也许g ++ ABI设计师可以发表评论?
有趣的是,这个例子表明,轻微可复制并不意味着A : X
等同于memcpy(&b, &a, sizeof b)
!