使用联盟的一个字段的地址来访问另一个字段是否合法?

Hol*_*Cat 10 c++

考虑以下代码:

union U
{
    int a;
    float b;
};

int main()
{
    U u;
    int *p = &u.a;
    *(float *)p = 1.0f; // <-- this line
}
Run Code Online (Sandbox Code Playgroud)

我们都知道联合字段的地址通常是相同的,但是我不确定它是否是定义良好的行为来做这样的事情.

因此,问题是:在上面的代码中,如何转换和取消引用指向union字段的指针是合法且定义明确的行为吗?


PS我知道它比C++更多C,但我试图理解它是否在C++中是合法的,而不是C.

Fil*_*efp 6

工会的所有成员必须居住在同一地址,这是由标准保证的.你所做的确实是明确定义的行为,但应该注意的是,你不能使用相同的方法从联盟的非活动成员中读取.

注意:不要使用c风格的演员表,reinterpret_cast在这种情况下更喜欢.


只要您所做的只是写入其他数据成员union,行为就是明确定义的; 但正如所述,这种变化被认为是其中的积极成员union; 这意味着你以后只能阅读你刚才写的内容.

union U {
    int a;
    float b;
};

int main () {
    U u;
    int *p = &u.a;
    reinterpret_cast<float*> (p) = 1.0f; // ok, well-defined
}
Run Code Online (Sandbox Code Playgroud)

注意:当涉及布局兼容类型时,上述规则有一个例外.


这个问题可以改写成下面的代码片段,它在语义上等同于"问题"的简化版本.

#include <type_traits>
#include <algorithm>
#include <cassert>

int main () {
  using union_storage_t = std::aligned_storage<
    std::max ( sizeof(int),   sizeof(float)),
    std::max (alignof(int),  alignof(float))
  >::type;

  union_storage_t u;

  int   * p1 = reinterpret_cast<  int*> (&u);
  float * p2 = reinterpret_cast<float*> (p1);
  float * p3 = reinterpret_cast<float*> (&u);

  assert (p2 == p3); // will never fire
}
Run Code Online (Sandbox Code Playgroud)

标准(n3797)说什么?

9.5/1    Unions    [class.union]
Run Code Online (Sandbox Code Playgroud)

在联合中,至多一个非静态数据成员可以在任何时间处于活动状态,即,至多一个非静态数据成员的值可以随时存储在并集中.[...] union的大小足以包含其中最大的非静态数据成员.每个非静态数据成员都被分配,就好像它是结构的唯一成员一样.union对象的所有非静态数据成员都具有相同的地址.

注意:C++ 11(n3337)中的措辞未被指定,即使意图始终是C++ 14的意图.