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

时间:2015-10-10 16:44:31

标签: c++

请考虑以下代码:

union U
{
    int a;
    float b;
};

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

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

所以,问题是:在上面的代码中,如何投射和取消引用指向union字段的指针是否合法且定义明确?

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

2 个答案:

答案 0 :(得分: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
}
  

注意:对于布局兼容的类型,上述规则有例外。

这个问题可以改写为以下代码片段,它在语义上等同于&#34;问题&#34;的简化版本。

#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
}

标准(n3797)说什么?

9.5/1    Unions    [class.union]
     
    

在联合中,最多只有一个非静态数据成员可以     在任何时候都是活跃的,也就是说,最多只有一个     非静态数据可以随时存储在并集中。     [...]联合的大小足以包含最大的     其非静态数据成员。每个非静态数据成员都是     分配好像它是结构的唯一成员。 union对象的所有非静态数据成员都具有相同的地址。

  
  

注意:C ++ 11中的措辞(n3337)未被指定,即使意图一直是C ++ 14的意图。

答案 1 :(得分:3)

是的,这是合法的。使用显式强制转换,您几乎可以做任何事情。

正如其他评论所述,union中的所有成员都在相同的地址/位置开始,因此向不同的成员投射指针是没有意义的。

汇编语言将是相同的。你想让代码易于阅读,所以我不推荐这种做法。令人困惑,没有任何好处。

另外,我推荐&#34;类型&#34;字段,以便您知道数据何时采用float格式与int格式。