【问题标题】:Assignment between union members工会成员之间的分配
【发布时间】:2015-08-05 05:09:03
【问题描述】:

这段代码定义明确吗?

int main()    
{    
    union     
    {    
        int i;    
        float f;    
    } u;    

    u.f = 5.0;    
    u.i = u.f;       // ?????
}    

它在一个表达式中访问两个不同的工会成员,所以我想知道它是否违反了 [class.union]/1 关于工会的活动成员的规定。

C++ 标准似乎没有明确说明哪些操作会更改内置类型的活动成员,以及如果读取或写入非活动成员会发生什么。

【问题讨论】:

  • 第二个分配不会让u.i 成为活跃成员而u.f 成为非活跃成员吗?
  • 否:如果一个函数被声明为返回一个值,但没有,这是未定义的行为(我开玩笑,我假设你在谈论 union 赋值)。
  • 我想说的是,由于u.f 的值计算是在赋值的副作用之前排序的,所以在任何时候都不会模棱两可哪个成员是活动成员,而且在任何时候time 是正在读取的非活动成员。相关语句的行为应与auto temp = u.f; u.i = temp; 相同
  • @Tas main 比较特殊,见 3.6.1/5

标签: c++ language-lawyer unions


【解决方案1】:

赋值运算符 (=) 和复合赋值运算符都从右到左分组。 [...] 在所有情况下,赋值之后左右操作数的值计算之后,在值计算之前进行排序的赋值表达式。 [...]

[N4431 §5.18/1]

在您的情况下,左侧的值计算(仅涉及成员访问,而不是读取)不会导致未定义的行为的前提下,我将应用上述内容如下:

  1. 右侧的值计算读取u.f,是联合体的活跃成员。所以一切都很好。
  2. 分配已执行。这涉及将获得的结果写入u.i,这现在会更改联合的活动成员。

【讨论】:

  • 但是左边的u.i是在赋值之前评估的,此时活动成员仍然是u.f。关于工会的措辞不清楚。
  • 措辞确实不清楚。但我认为只有从非活动成员读取数据才不确定,并且左侧的评估不涉及读取u.i
【解决方案2】:

可以写入非活动成员。这实际上是使它们活跃的唯一方法。活跃会员没有任何限制;它可以被读取和写入。

可以获取非活动成员的地址。这是执行写入的有效方式:

union {
  int i;
  float f;
} u;
float* pf = &u.f; // Does NOT change the active member.
u.i = 3;
*pf = 3.0; // Changes the active member.

在您的示例中,活动成员只能通过向其写入5 变为u.i,这意味着必须已读取值u.f

【讨论】:

  • 这假定读和写是按顺序发生的;特别是,它假设 rhs 的评估是在写入 lhs 之前排序的。我相信这是真的:是吗?
  • 有人说这违反了严格的别名。如果你问我,这是一个疯狂的想法,但他们在 GCC 上工作......
  • @curiousguy:什么精确的部分? GCC 正确地抱怨 reading 通过另一种类型。合乎逻辑,因为这不会更改活动成员并且是类型违规(读取非活动成员)。
  • @MSalters 文档说写入不兼容类型的部分(如int/float)可以重新排序。开发人员说您不能在标准 C/C++ 中编写类似 malloc 的部分。
  • @curiousguy:海湾合作委员会的人很有侵略性,但通常他们并不愚蠢。 C 和 C++ 具有联合的“活动成员”的概念,这是最后写入的成员。重新排序写入会破坏活动成员。至于malloc,我完全愿意相信您不能在标准C 中编写有竞争力的 malloc 实现。严格来说,您不能在标准C 中编写任何malloc 替换无论如何,标准C;它从哪里获得记忆?总是隐含着某种程度的不可移植性。
【解决方案3】:

通过查看[class.union]/1,从包含标准布局联合的标准布局联合的考虑共享一个共同的初始序列,这意味着以下是可以的

int main()
{
   union 
   {
      union 
      {
          int x ;
          float y ;
      } v ;
      union 
      {
          int x ;
          double z ;
      } w ;      
   } u ;

   u.v.x = 2 ;
   int n = u.w.x ; // n contains 2
}

这里常用的初始序列是int x ;

这不是确切的问题,但告诉我们这种混合是可以的。

【讨论】:

  • A standard-layout struct is a standard-layout class defined with the class-key struct or the class-key class. 通用初始序列保证不适用于unions
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-06-13
  • 1970-01-01
  • 2018-07-11
  • 2014-03-24
相关资源
最近更新 更多