c++ - 如何用 0 memset 匿名 union

标签 c++ initialization c++14 unions memset

我应该如何将匿名 union 归零?我在 cppreference 上找不到任何内容关于它的页面。 memset它最大的成员 0 在这里工作吗?

例如-

#include <iostream>
#include <cstring>

struct s{
    char a;
    char b[100];
};

int main(){
 union {
   int a;
   s b;
   char c;
 };

  // b.a = 'a'; (1)

  std::memset(&b, 0, sizeof(b));

  std::cout << a << "\n";
  std::cout << b.a << " " << b.b << "\n";
  std::cout << c << "\n";
}

此外,如果这行得通,我是否应该在使用 memset() 激活最大成员之前取消注释 (1)?

最佳答案

如果你真的想尊重标准,你应该知道你写的代码是未定义的行为:C++标准§3.8 [basic.life]:

... except that if the object is a union member or subobject thereof, its lifetime only begins if that union member is the initialized member in the union (8.6.1, 12.6.2), or as described in 9.3. The lifetime of an object o of type T ends when: (1.3) — if T is a class type with a non-trivial destructor (12.4), the destructor call starts, or (1.4) — the storage which the object occupies is released, or is reused by an object that is not nested within o (1.8).

在 §9.3 中解释了您可以通过分配给标准布局 union 的成员来激活它。它还解释了您可以探索 union 成员的值(value),该 union 只有在满足某些条件时才会被激活:

If a standard-layout union contains several standard-layout structs that share a common initial sequence (9.2), and if a non-static data member of an object of this standard-layout union type is active and is one of the standard-layout structs, it is permitted to inspect the common initial sequence of any of the standard-layout struct members; see 9.2. — end note ]

所以当你写std::cout<< a << "\n"你还没有初始化a ,或通过赋值激活它,并且没有成员被初始化,所以你处于未定义的行为(注意:但我知道的编译器支持它,至少在 PC 上,作为标准的扩展。 )

所以在使用之前a你必须写a=0 , 或制作 a union 的初始化成员,因为a不与 b 共享公共(public)初始化序列也不c .

所以如果你使用memset 正如 MSalters 的回答中所建议的,无论您做什么,您都必须在使用之前将某些东西分配给 union 成员。如果想保持定义的行为,请不要使用 memset .注意 memset可以安全地与非 union 成员的标准布局对象一起使用,因为它们的生命周期从为它们获取存储时开始。


总而言之,要保持已定义的行为,您必须至少初始化一个成员,然后您可以检查 union 体中与初始化成员共享公共(public)初始化序列的其他成员。

  1. 如果您打算在主函数中使用匿名 union , 您可以声明 union 静态:所有静态对象都是零初始化的。 (但当您记忆起 main() 不会发生的功能时,不会重新初始化):

    int main(){
     static union {
      s b;
      int a;
      char c;
      };
     //...
     }
    

    如 C++ 标准 §8.6 文章 (6.3) [dcl.init] 中所述:

    if T is a (possibly cv-qualified) union type, the object’s first non-static named data member is zero- initialized and padding is initialized to zero bits;

  2. 否则,如果结构成员 (s) 之间没有填充,则可以使用空列表聚合初始化较大的成员 (s):

    //...
    int main(){
      union {
       int a;
       s b{};
       char c;
       };
      //...
      }
    

    之所以可行,是因为 union 的所有成员都是一致的。因此,如果 s 的成员之间没有填充, union 内存的每个字节都将初始化为零,C++ 标准 §9.3 [class.union] 第 2 条:

    The size of a union is sufficient to contain the largest of its non-static data members. Each non-static data member is allocated as if it were the sole member of a struct. [ Note: A union object and its non-static data members are pointer-interconvertible (3.9.2, 5.2.9). As a consequence, all non-static data members of a union object have the same address.

  3. 如果S内部有padding,那么就声明一个char数组来初始化:

    //...
    int main(){
      union {
       char _initialization[sizeof(s)]{};
       int a;
       s b;
       char c;
       };
      //...
      }
    

注意:使用您的示例,或最后两个代码示例,以及使用 memset 的代码生成完全相同的初始化指令集(clang -> x86_64):

    pushq   %r14
    pushq   %rbx
    subq    $120, %rsp
    xorps   %xmm0, %xmm0
    movaps  %xmm0, 96(%rsp)
    movaps  %xmm0, 80(%rsp)
    movaps  %xmm0, 64(%rsp)
    movaps  %xmm0, 48(%rsp)
    movaps  %xmm0, 32(%rsp)
    movaps  %xmm0, 16(%rsp)
    movq    $0, 109(%rsp)

关于c++ - 如何用 0 memset 匿名 union ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/42361656/

相关文章:

c++ - 传递静态 constexpr 成员而没有按值定义的奇怪行为

c++ - 为什么具有推导返回类型的模板不能与它的其他版本重载?

c++ - C++ 中干净、功能性的脚本式错误处理

c++ - Windows Vista/7 中的控制台/命令提示符全屏

c++ - Visual Studio TODO/任务列表未显示

c++ - 如何初始化一个很长的数组?

c++ - boost char_ptr_holder 实例化

Android adb 和 C++

c++ - 使用私有(private)变量初始化数组

c++ - 在拥有的翻译单元发生静态初始化之前,文件范围的静态对象是否已初始化为零?