【问题标题】:I have a union of arrays. Is it okay to read some elements from one array and other elements from another array? [duplicate]我有一个数组联合。可以从一个数组中读取一些元素,从另一个数组中读取其他元素吗? [复制]
【发布时间】:2019-12-08 07:24:13
【问题描述】:

我有以下工会:

union PM_word {
    uint8_t  u8[8];
    uint16_t u16[4];
    uint32_t u32[2];
    uint64_t u64[1];
};

假设我初始化这个联合的一个实例如下:

PM_word word;
word.u32[0] = 0;
word.u16[2] = 1;
word.u8[6] = 2;
word.u8[7] = 3;

如果我理解正确,尝试读取word.u32[1] 是未定义的行为,因为已设置的word.u16word.u8 的元素与其重叠。但是,读取word.u32[0] 是否也是未定义的行为?

编辑: 也重新标记了 C++。如果 C 和 C++ 的语义在这个问题上有所不同,非常感谢有关 C 和 C++ 的答案。

【问题讨论】:

  • 如果你没有明确设置值,它的值将是未定义的。
  • 由于内存重叠,改变u16或u8意味着读取u32是未定义的。在实践中,您仍然可以读取 u32,根据您设置的 u16 和 u8 值,它可能会有所帮助。
  • 它真正依赖于平台。不同的计算机会以不同的方式排列内存,因此您将从 u32 读回的实际值会因特定计算机而异
  • 据我在 C++ 中的理解,你不能这样做。您必须将一个数组 memcpy 放入您想要访问它的任何数组中,以便在 C++ 中定义行为。 C++ 不做类型双关语。
  • C 和 C++ 在这一点上有所不同。您不应在一个问题中同时标记两者。

标签: c++ c


【解决方案1】:

相关C++标准引用:

[class.union]

在联合中,如果非静态数据成员的名称引用其生命周期已经开始但尚未结束的对象 ([basic.life]),则它是活动的。 union 类型的对象的非静态数据成员在任何时候最多可以有一个是活动的,即任何时候最多可以将一个非静态数据成员的值存储在一个 union 中。 [... 关于具有公共初始序列的类的特殊情况,不适用于本示例,因为联合没有类类型的成员 ...]

...

当赋值运算符的左操作数涉及指定联合成员的成员访问表达式 ([expr.ref]) 时,它可能会开始该联合成员的生命周期,如下所述。 对于表达式 E,定义 E 的子表达式的集合 S(E) 如下:

  • 如果 E 的形式为 A[B] 并被解释为内置数组下标运算符,如果 A 是数组类型,则 S(E) 为 S(A),如果 B 为 S(B)是数组类型,否则为空。

  • [... 其他情况不相关,因为引用的情况适用 ...]

在 E1 = E2 形式的赋值表达式中,对 S 的每个元素 X 使用内置赋值运算符 ([expr.ass]) 或普通赋值运算符 ([class.copy.assign]) (E1),如果在 [basic.life] 下修改 X 将具有未定义的行为,则在指定存储中隐式创建 X 类型的对象;不执行初始化,其生命周期的开始在左右操作数的值计算之后和赋值之前排序。 [ 注意:这将结束先前活跃的工会成员的生命周期,如果有的话([basic.life])。 —— 尾注 ]

这意味着在word.u8[6] = 2; 语句之后,只有word.u8 成员处于活动状态,而所有其他工会成员都处于非活动状态。在最后一条语句word.u8[7] = 3; 之后,只有word.u8[6]word.u8[7] 有初始化值。

非活动成员的生命周期已结束。以下是关于是否可以读取它们的值的相关规则:

[基本生活]

类似地,在对象的生命周期开始之前但在对象将占用的存储空间已分配之后,或者在对象的生命周期结束之后并且在对象占用的存储空间被重用或释放之前,任何glvalue可以使用指代原始对象的那个,但只能以有限的方式使用。 ...如果出现以下情况,则程序具有未定义的行为:

  • glvalue 用于访问对象,或者
  • ...

所以,是的。访问非活动成员将是 UB(除非该访问是分配,或者在涉及常见的类初始序列的特殊情况下,根据 [class.union])。

【讨论】:

    【解决方案2】:

    我会说行为不是未定义的,只是不是你所期望的...... 因为 - 正如你所指出的 - 内存重叠,你不能一次使用(设置)。 但例如同时使用word.u8[0]word.u8[1]word.u16[1] 会起作用。 只要您使用word.u64[0],所有其他变量都会更改。它们是否损坏取决于值...

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2014-03-25
      • 2014-07-26
      • 1970-01-01
      • 1970-01-01
      • 2018-12-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多