扩展填充结构时,为什么不能在尾部填充中放置额外的字段?
让我们考虑结构:
struct S1 {
int a;
char b;
};
struct S2 {
struct S1 s; /* struct needed to make this compile as C without typedef */
char c;
};
// For the C++ fans
struct S3 : S1 {
char c;
};
S1 的大小为 8,这是由于对齐的原因.但是 S2 和 S3 的大小是 12.这意味着编译器将它们构造为:
The size of S1 is 8, which is expected due to alignment. But the size of S2 and S3 is 12. Which means the compiler structure them as :
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10| 11|
| a | b | padding | c | padding |
编译器可以在不破坏对齐约束的情况下将 c 放在 6 7 8 的填充中.阻止它的规则是什么,背后的原因是什么?
The compiler could place c in the padding in 6 7 8 without breaking alignment constraints. What is the rule that prevent it, and what is the reason behind it ?
推荐答案
简短回答(针对问题的 C++ 部分): Itanium ABI for C++ 由于历史原因,禁止使用基的尾部填充POD 类型的子对象.请注意,C++11 没有这样的禁令.相关规则 3.9/2 允许通过其底层表示复制可平凡复制的类型,明确排除基础子对象.
Short answer (for the C++ part of the question): The Itanium ABI for C++ prohibits, for historical reasons, using the tail padding of a base subobject of POD type. Note that C++11 does not have such a prohibition. The relevant rule 3.9/2 that allows trivially-copyable types to be copied via their underlying representation explicitly excludes base subobjects.
长答案:我会尝试同时处理 C++11 和 C.
Long answer: I will try and treat C++11 and C at once.
S1
的布局必须包含填充,因为S1::a
必须与int
对齐,并且数组S1[N]
由连续分配的S1
类型的对象组成,每个对象的a
成员必须如此对齐.- 在 C++ 中,非基本子对象的可简单复制类型
T
的对象可以被视为sizeof(T)
字节的数组(即,您可以将指向unsigned char *
的对象指针,并将结果作为指向unsigned char[sizeof(T)]
的第一个元素的指针,这个数组的值决定了物体).由于 C 中的所有对象都是这种类型,这就解释了 C 和 C++ 的S2
. - C++ 剩下的有趣案例是:
- The layout of
S1
must include padding, sinceS1::a
must be aligned forint
, and an arrayS1[N]
consists of contiguously allocated objects of typeS1
, each of whosea
member must be so aligned. - In C++, objects of a trivially-copyable type
T
that are not base subobjects can be treated as arrays ofsizeof(T)
bytes (i.e. you can cast an object pointer to anunsigned char *
and treat the result as a pointer to the first element of aunsigned char[sizeof(T)]
, and the value of this array determines the object). Since all objects in C are of this kind, this explainsS2
for C and C++. - The interesting cases remaining for C++ are:
- 不受上述规则约束的基础子对象(参见 C++11 3.9/2),以及
- 任何非普通可复制类型的对象.
对于 3.1,确实存在常见的、流行的基本布局优化",其中编译器将类的数据成员压缩"到基本子对象中.当基类为空时(∞% 大小减少!),这是最引人注目的,但更普遍地适用.但是,当相应的基本类型是 POD 时,我在上面链接的用于 C++ 的 Itanium ABI 以及许多编译器实现的 C++ ABI 禁止这种尾部填充压缩(POD 意味着可简单复制和标准布局).
For 3.1, there are indeed common, popular "base layout optimizations" in which compilers "compress" the data members of a class into the base subobjects. This is most striking when the base class is empty (∞% size reduction!), but applies more generally. However, the Itanium ABI for C++ which I linked above and which many compilers implement forbids such tail padding compression when the respective base type is POD (and POD means trivially-copyable and standard-layout).
对于 3.2,Itanium ABI 的相同部分适用,尽管我目前不认为 C++11 标准实际上要求任意的、非平凡可复制的 member 对象必须具有与相同类型的完整对象相同的大小.
For 3.2 the same part of the Itanium ABI applies, though I don't currently believe that the C++11 standard actually mandates that arbitrary, non-trivially-copyable member objects must have the same size as a complete object of the same type.
保留以前的答案以供参考.
我相信这是因为 S1
是标准布局,因此出于某种原因,S3
的 S1
-子对象保持不变.我不确定标准是否要求这样做.
I believe this is because S1
is standard-layout, and so for some reason the S1
-subobject of S3
remains untouched. I'm not sure if that's mandated by the standard.
然而,如果我们将 S1
变成非标准布局,我们观察到布局优化:
However, if we turn S1
into non-standard layout, we observe a layout optimization:
struct EB { };
struct S1 : EB { // not standard-layout
EB eb;
int a;
char b;
};
struct S3 : S1 {
char c;
};
现在 sizeof(S1) == sizeof(S3) == 12
在我的平台上.现场演示.
Now sizeof(S1) == sizeof(S3) == 12
on my platform. Live demo.
这是一个更简单的例子:
struct S1 {
private:
int a;
public:
char b;
};
struct S3 : S1 {
char c;
};
混合访问使 S1
成为非标准布局.(现在 sizeof(S1) == sizeof(S3) == 8
.)
The mixed access makes S1
non-standard-layout. (Now sizeof(S1) == sizeof(S3) == 8
.)
更新:定义因素似乎是平凡以及标准布局,即类必须是 POD.以下非 POD 标准布局类是基础布局可优化的:
Update: The defining factor seems to be triviality as well as standard-layoutness, i.e. the class must be POD. The following non-POD standard-layout class is base-layout optimizable:
struct S1 {
~S1(){}
int a;
char b;
};
struct S3 : S1 {
char c;
};
再次sizeof(S1) == sizeof(S3) == 8
.演示
相关文章