【问题标题】:Is sizeof(T) == sizeof(int)?sizeof(T) == sizeof(int) 吗?
【发布时间】:2014-02-01 14:16:59
【问题描述】:

我一直在研究标准草案,但似乎找不到我想要的东西。

如果我有标准布局类型

struct T {
   unsigned handle;
};

那我知道reinterpret_cast<unsigned*>(&t) == &t.handle 有一些T t;

目标是创建一些vector<T> v 并将&v[0] 传递给需要指向无符号整数数组的指针的C 函数。

那么,标准是否定义了sizeof(T) == sizeof(unsigned),这是否意味着T 的数组与unsigned 的数组具有相同的布局?

虽然this question 解决了一个非常相似的主题,但我询问的是数据成员和类都是标准布局,而数据成员是基本类型的具体情况。

我读过一些段落,似乎暗示也许这可能是真的,但没有什么能打中钉子的。例如:

第 9.2.17 节

两个标准布局结构(第 9 条)类型是布局兼容的,如果 它们具有相同数量的非静态数据成员和对应的 非静态数据成员(按声明顺序)具有布局兼容 类型

这不是我想要的,我不认为。

【问题讨论】:

  • 为什么不只是 vector::data() ?
  • @DieterLücking 查看编辑。
  • 对于 int 它可能是有效的,但通常在 'handle' 之后可能会有尾随填充。
  • Here's 一个人为的反例。
  • 我猜你正在寻找一个根本不存在的保证。无论布局类型如何,结构的末尾都可能有填充。

标签: c++ arrays sizeof


【解决方案1】:

你基本上是在问,给定:

struct T {
    U handle;
};

是否保证sizeof(T) == sizeof(U).不,不是。

ISO C++03 标准的第 9.2/17 节说:

指向 POD 结构对象的指针,使用 reinterpret_cast,指向它的初始成员(或者如果该成员是 位域,然后到它所在的单元),反之亦然。

假设您有一个struct T 数组。反之亦然部分意味着任何T::handle 成员的地址也必须是struct T 的有效地址。现在,假设这些成员的类型为char,并且您的声明是正确的。这意味着struct T 将被允许有一个未对齐的地址,这似乎不太可能。该标准通常试图不以这种方式束缚实现的双手。为了使您的声明属实,该标准必须要求允许 struct T 具有未对齐的地址。所有结构都必须允许它,因为struct T 可能是前向声明的不透明类型。

此外,第 9.2/17 节继续说明:

[注意:因此,在 POD 结构对象中可能有未命名的填充,但不是在其开头,这是实现适当对齐所必需的。]

换一种说法,这意味着不能保证永远不会有填充。

【讨论】:

  • 如果T::handlechar 类型,那么alignof(T::handle)alignof(char) == 1。因此,Tany 对齐将导致 handlesT 的数组中对齐 - 实际上,handle 不能未对齐。
  • @jamesdlin 我明白你的意思,这很糟糕。我希望对标准布局/整数/基本对齐等有一些警告。嗯。我添加了一些编译时间检查sizeof(T) == sizeof(U),如果不是,则进行很长的调用。
【解决方案2】:

我已经习惯了 Borland 环境和他们:

在您的情况下,T 是一个结构,因此 sizeof(T) 是结构的大小

  • 这取决于编译器的 #pragma packalign 设置
  • 所以有时它可能大于 sizeof(unsigned) !!!

同样的原因,如果你有 4Byte struct (uint32) 和 16Byte allign

  • 结构 T { uint32 u; };
  • 那么T a[100] 不等于uint32 a[100];
  • 因为 T 是 uint32 + 12 字节空白空间!!!

【讨论】:

    【解决方案3】:

    撤回:论点是错误的。引理 2 的证明依赖于一个隐藏的前提,即聚合类型的对齐方式严格取决于其成员类型的对齐方式。正如Dyp points out in the commentary,标准不支持该前提。因此,struct { Foo f } 可以接受比Foo 更严格的对齐要求。


    我将在这里扮演魔鬼的拥护者,因为似乎没有其他人愿意。我将争辩说,标准 C++ (I'll refer to N3797 herein) 保证 sizeof(T) == sizeof(U)T 是标准布局类 (9/7) 时,默认对齐具有单个默认对齐的非静态数据成员 U,例如,
    struct T { // or class, or union
      U u;
    };
    

    这是公认的:

    • sizeof(T) >= sizeof(U)
    • offsetof(T, u) == 0 (9.2/19)
    • U 必须是标准布局类型,T 才能成为标准布局类
    • u 具有完全由 sizeof(U) 连续内存字节组成的表示 (1.8/5)

    这些事实表明T 的表示的第一个sizeof(U) 字节被u 的表示占据。如果sizeof(T) > sizeof(U),则多余的字节必须是tail padding:在Tu 的表示之后插入未使用的填充字节。

    简而言之,论据是:

    • 该标准详细说明了实现可以向标准布局类添加填充的情况,
    • 这些情况均不适用于此特定情况,因此
    • 符合标准的实现可能不会添加填充。

    填充的潜在来源

    在什么情况下,标准允许实现将这种填充添加到标准布局类的表示中?当需要对齐时:根据 3.11/1,“alignment 是实现定义的整数值,表示可以分配给定对象的连续地址之间的字节数。”有两次提到添加填充,都是出于对齐原因:

    • 5.3.3 Sizeof [expr.sizeof]/2 声明“当应用于引用或引用类型时,结果是被引用类型的大小。应用时 对于一个类,结果是该类的对象中的字节数,包括将该类型的对象放入数组中所需的任何填充。最派生类的大小应大于零 (1.8)。将sizeof 应用于基类子对象的结果是基类类型的大小。77 当应用于数组时,结果是数组中的总字节数。这意味着 n 个元素的数组的大小是元素大小的 n 倍。”

    • 9.2 类成员 [class.mem]/13 声明“实现对齐要求可能会导致两个相邻的成员不会立即彼此分配;管理虚拟功能 (10.3) 和虚拟基的空间要求也可能如此类 (10.1)。”

    • (值得注意的是,C++ 标准包含一个总括语句,允许实现在结构中插入填充,如 C 标准中那样,例如,N1570 (C11-ish) §6.7.2.1/ 15 “结构对象内可能有未命名的填充,但不是在其开头。”和 /17 “结构或联合的末尾可能有未命名的填充。”)

    显然 9.2 的文本不适用于我们的问题,因为 (a) T 只有一个成员,因此没有“相邻成员”,并且 (b) T 是标准布局,因此没有虚拟函数或虚拟基类(每 9/7)。证明 5.3.3/2 不允许在我们的问题中进行填充更具挑战性。


    一些先决条件

    引理 1: 对于任何具有默认对齐方式的 W 类型,alignof(W) 除以 sizeof(W) 数组大小除以 5.3.3/2 W 类型的 n 个元素恰好是 sizeof(W)n 倍(即,数组元素之间没有“外部”填充)。然后,连续数组元素的地址相隔sizeof(W) 个字节。根据对齐的定义,那么alignof(W) 一定会除以sizeof(W)

    引理 2: 只有默认对齐数据成员的默认对齐标准布局类W 的对齐alignof(W) 是对齐的最小公倍数LCM(W)数据成员的个数(如果没有,则为 1): 给定一个可以分配 W 对象的地址,地址 LCM(W) 字节之外的地址也必须适当对齐:地址之间的差异成员子对象的数量也将是LCM(W) 字节,并且每个此类成员子对象的对齐方式将LCM(W) 分开。根据 3.11/1 中对齐的定义,我们有 alignof(W) 除以 LCM(W)。任何整数字节 n < LCM(W) 都不能被 W 的某些成员 v 的对齐整除,因此距离 W 的对象的地址只有 n 字节的地址因此,对于W 的对象,即alignof(W) >= LCM(W),分配的没有适当地对齐。鉴于alignof(W)LCM(W)alignof(W) >= LCM(W) 分开,我们就有alignof(W) == LCM(W)


    结论

    将这个引理应用于原始问题具有alignof(T) == alignof(U) 的直接后果。那么“将这种类型的对象放入数组中可能需要多少填充”? 。由于alignof(T) == alignof(U) 被第二个引理除以alignof(U)sizeof(U) 除以第一个引理,因此alignof(T) 必须除以sizeof(U) 所以需要零字节的填充来将T 类型的对象放入数组中.

    由于已消除了所有可能的填充字节来源,因此实现可能不会向T 添加填充,我们根据需要添加了sizeof(T) == sizeof(U)

    【讨论】:

    • 您能否详细说明 3.11/1 如何要求 alignof(W) 划分 LCM(W)? IE。为什么它不允许过度对齐(不是在扩展对齐的意义上,而是例如alignof(W) == 2*LCM(W))?
    • 3.11/1 说对齐是“...连续地址之间可以分配给定对象的字节数。”如果foo 是可接受的,那么下一个导致正确对齐成员的连续 地址是foo + LCM(W)2 * LCM(W) 显然会适当对齐,但不是连续的。
    • 我认为这需要将 “可以分配” 解释为对实现施加的限制。如果您将其解释为对程序施加的限制,2*LCM(W) 可能是可能的(即实现要求程序“过度对齐”该组合类型的对象,即使硬件可以支持较弱的对齐)。
    • @dyp 好的,我想我现在明白你的意思了。我假设聚合类型的对齐仅由其成员的对齐要求决定。您建议实现对聚合的要求可能比对齐成员的要求更严格。
    • @dyp 检查后,我同意该标准承认该解释。感谢您的评论并指出隐藏的前提。
    猜你喜欢
    • 2010-11-09
    • 2013-02-19
    • 1970-01-01
    • 2016-02-25
    • 1970-01-01
    • 2017-03-26
    • 2013-09-24
    • 2016-12-12
    相关资源
    最近更新 更多