扩展填充结构时,为什么不能在尾部填充中放置额外的字段? [英] When extending a padded struct, why can't extra fields be placed in the tail padding?

查看:22
本文介绍了扩展填充结构时,为什么不能在尾部填充中放置额外的字段?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

让我们考虑结构:

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.

  1. S1 的布局必须包含填充,因为 S1::a 必须与 int 对齐,并且数组 S1[N] 由连续分配的 S1 类型的对象组成,每个对象的 a 成员必须如此对齐.
  2. 在 C++ 中,非基本子对象的可简单复制类型 T 的对象可以被视为 sizeof(T) 字节的数组(即,您可以将指向unsigned char *的对象指针,并将结果作为指向unsigned char[sizeof(T)]的第一个元素的指针,这个数组的值决定了物体).由于 C 中的所有对象都是这种类型,这就解释了 C 和 C++ 的 S2.
  3. C++ 剩下的有趣案例是:
  1. The layout of S1 must include padding, since S1::a must be aligned for int, and an array S1[N] consists of contiguously allocated objects of type S1, each of whose a member must be so aligned.
  2. In C++, objects of a trivially-copyable type T that are not base subobjects can be treated as arrays of sizeof(T) bytes (i.e. you can cast an object pointer to an unsigned char * and treat the result as a pointer to the first element of a unsigned char[sizeof(T)], and the value of this array determines the object). Since all objects in C are of this kind, this explains S2 for C and C++.
  3. The interesting cases remaining for C++ are:
  1. 不受上述规则约束的基础子对象(参见 C++11 3.9/2),以及
  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 是标准布局,因此出于某种原因,S3S1-子对象保持不变.我不确定标准是否要求这样做.

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.演示

这篇关于扩展填充结构时,为什么不能在尾部填充中放置额外的字段?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆