当伸出的填充结构,为什么不能额外的字段放置在尾部填充? [英] When extending a padded struct, why can't extra fields be placed in the tail padding?
问题描述
让我们考虑结构:
结构S1 {
int类型的;
炭B:
};结构S2 {
结构S1 S; / *结构,使这个编译为C无必要的typedef * /
焦炭℃;
};//对于C ++球迷
结构S3:S1 {
焦炭℃;
};
S1的大小是8,这是由于对准预期。但是S2和S3的大小为12。这意味着,编译器结构它们作为
| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| A | C |填充| B |填充|
编译器可以将C在6 7 8填充不破坏对齐约束。那是什么prevent它的规则,什么是它背后的原因是什么?
简短的回答(针对C ++的问题的一部分)的:的的安腾ABI的C ++ 禁止,由于历史的原因,采用POD类型的基子对象的尾部填充。需要注意的是C ++ 11没有这样的禁令。相关规则3.9 / 2,让平凡的,可复制的类型通过它们的底层重新presentation被复制明确排除基地的子对象。
龙答:我会尽量把C ++ 11和C马上
-
S1
的布局必须包括填充,因为S1 ::一
必须对齐INT
,和一个数组S1 [N]
包含类型的连续分配对象的S1
,其各自的A
成员必须如此一致。 - 在C ++中,一个平凡的,复制的类型的对象
T
未基地子对象可以被视为sizeof的的阵列(T)
字节(即你可以施放对象指向一个无符号字符*
和治疗结果作为指针无符号的char [sizeof的(T)]
,而这个数组的值确定的对象)。由于C中的所有对象都是这样的,这说明S2
C和C ++。 - 剩余C ++有趣的情况是:
- 基地子对象,它不受上述规则(见C ++ 11 3.9 / 2),和
- 这不是平凡,复制的类型的对象。
有关3.1,确有普通,流行的基地布局的优化,即编译器COM preSS一类的数据成员进入基地的子对象。这是最引人注目的,当基类是空的(∞%!体积减少),但应用更普遍。但是,安腾ABI的C ++,我上面链接和许多编译器实现者禁用这种尾部填充COM pression当相应的基本类型是POD(和POD意味着平凡,可复制和标准布局)。
有关3.2安腾ABI的相同部分适用,虽然我目前不认为C ++ 11的标准实际上是强制要求任意的,非平凡的,可复制的成员的对象必须具备大小相同的相同类型的一个完整的对象
的 previous答案保留,以供参考。的
我相信这是因为 S1
为标准布局,因此出于某种原因, S1
的-subobject S3
保持不变。我不知道这是由标准规定的。
但是,如果我们把 S1
成非标准布局,我们观察到一个布局优化:
结构EB {};结构S1:EB {//不是标准布局
EB EB;
int类型的;
炭B:
};结构S3:S1 {
焦炭℃;
};
现在的sizeof(S1)==的sizeof(S3)== 12
我的平台上。 现场演示。
这里是一个简单的例子的:
结构S1 {
私人的:
int类型的;
上市:
炭B:
};结构S3:S1 {
焦炭℃;
};
混合接入品牌 S1
非标准布局。 (现在的sizeof(S1)==的sizeof(S3)== 8
)
更新:的决定性因素似乎是的平凡的以及标准layoutness,即类必须是POD。以下非POD的标准布局类是基本布局优化的:
结构S1 {
〜S1(){}
int类型的;
炭B:
};结构S3:S1 {
焦炭℃;
};
再次的sizeof(S1)==的sizeof(S3)== 8
。 演示
Let's consider the structs :
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;
};
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 |
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 ?
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.
Long answer: I will try and treat C++11 and C at once.
- 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:
- base subobjects, which are not subject to the above rule (cf. C++11 3.9/2), and
- any object that is not of trivially-copyable type.
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).
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.
Previous answer kept for reference.
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.
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;
};
Now sizeof(S1) == sizeof(S3) == 12
on my platform. Live demo.
And here is a simpler example:
struct S1 {
private:
int a;
public:
char b;
};
struct S3 : S1 {
char c;
};
The mixed access makes S1
non-standard-layout. (Now sizeof(S1) == sizeof(S3) == 8
.)
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;
};
Again sizeof(S1) == sizeof(S3) == 8
. Demo
这篇关于当伸出的填充结构,为什么不能额外的字段放置在尾部填充?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!