【问题标题】:Struct alignment and type reinterpretation结构对齐和类型重新解释
【发布时间】:2016-09-20 16:21:16
【问题描述】:

假设我有两种类型 A 和 B。

那我做这个类型

struct Pair{
    A a;
    B b;
};

现在我有了这样的功能。

void function(Pair& pair);

让我们假设function 只会使用这对中的a 部分。

那么以这种方式使用和调用函数是未定义的行为吗?

A a;
function(reinterpret_cast<Pair&>(a));

我知道编译器可以在成员之后插入填充字节,但它也可以在第一个成员之前插入吗?

【问题讨论】:

  • 如果函数只访问A部分,那为什么它需要Pair输入而不是A输入???
  • 其实这个例子是通用的,以便描述我的问题。我想知道这是否未定义。但假设它是实现一组通用行为的一组函数的一部分,并且必须具有相同的签名。

标签: c++ struct undefined-behavior


【解决方案1】:

我认为这是定义的行为,假设 Pair 是标准布局。否则,这是未定义的行为。

首先,一个标准布局类和它的第一个成员共享一个地址。 [basic.compound] 中的新措辞(阐明了之前的规则)如下:

两个对象 abpointer-interconvertible 如果:
* [...]
* 一个是标准布局类对象,另一个是该对象的第一个非静态数据成员, 或者,[...]
* [...]
如果两个对象是pointer-interconvertible,那么它们有相同的地址,并且有可能获得一个 通过reinterpret_cast (5.2.10) 从一个指向另一个的指针指向另一个。

也来自 [class.mem]:

如果标准布局类对象有任何非静态数据成员,其地址与地址相同 它的第一个非静态数据成员。否则,其地址与其第一个基类的地址相同 子对象(如果有)。

所以从APairreinterpret_cast 很好。如果然后function 只访问a 对象,那么该访问定义良好,因为A 的偏移量为0,因此行为相当于让function 直接获取A&amp;。显然,对b 的任何访问都是未定义的。


然而,虽然我相信代码是定义的行为,但这是个坏主意。这是定义的行为NOW,但有一天有人可能会将function 更改为引用pair.b,然后您将陷入痛苦的世界。简单地写会容易得多:

void function(A& a) { ... }
void function(Pair& p) { function(p.a); }

只需使用您的a 直接致电function

【讨论】:

  • 首先感谢您的回答。阅读有关标准布局类的信息,我看到对于具有标准布局的类,所有成员都必须具有相同的访问控制。我觉得很奇怪,如果 Pair 有第三个私人成员,会发生什么变化。为什么编译器会改变顺序?
  • @user183833 POD(标准布局的前身)旨在与 c 兼容。允许使用 C++ 特定结构的代码以它选择的任何方式来处理结构布局;它可以将私有数据放在首位、最后或中间。我认为它甚至可以将私有数据放在自己单独的内存空间中,但我不确定。当 POD 演变为标准布局时,一些要求被删除:每个编译器已经使带有构造函数的 POD 具有标准布局,因此它们以一种有用的方式编码现有实践
  • @Barry 至于你说的最后一件事。 Here 是一个用例。
【解决方案2】:

是的,这是未定义的行为。

在结构对中,a 和 b 之间可以有填充。对结构成员的赋值可以修改结构中的任何填充。因此,对 pair.a 的赋值可以修改它认为结构中有填充的内存,实际上在你的 a 占用的内存之后只有随机内存。

【讨论】:

  • 感谢您的回复。一件事如何分配给一个对象修改它之外的字节?它是如何以及为什么会发生的?
猜你喜欢
  • 1970-01-01
  • 2020-01-01
  • 2011-09-25
  • 2020-08-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-12-28
相关资源
最近更新 更多