如何使用0来设置匿名联合

Abh*_*yal 10 c++ initialization memset unions c++14

我应该如何将匿名联盟归零?我在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";
}
Run Code Online (Sandbox Code Playgroud)

如果这样可行,我应该在使用memset()激活最大的成员之前取消注释(1)吗?

Oli*_*liv 5

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

...除非如果对象是联合成员或其子对象,则只有当联合成员是联合中的初始化成员(8.6.1,12.6.2)或9.3中描述时,其生命周期才开始.类型T的对象o的生命周期结束时:(1.3) - 如果T是具有非平凡析构函数(12.4)的类类型,则析构函数调用开始,或者(1.4) - 释放对象所占用的存储空间,或由未嵌套在o(1.8)中的对象重用.

在§9.3中,解释了您可以通过分配标准布局联合的成员来激活它.它还解释了您可以探索联合成员的价值,该联盟成员仅在遵守某些标准时才会激活:

如果标准布局联合包含多个共享公共初始序列(9.2)的标准布局结构,并且此标准布局联合类型的对象的非静态数据成员是活动的并且是标准布局之一结构,允许检查任何标准布局结构成员的公共初始序列; 见9.2. - 结束说明]

因此,当您编写时,您std::cout<< a << "\n"尚未初始化a,或通过赋值激活它,并且没有成员已初始化,因此您处于未定义行为(Nota:但我知道的编译器支持它,至少在PC上,作为标准的扩展.)

所以,使用前a你将不得不写a=0,或使a工会的初始化成员,因为a不符合既不都有一个共同的初始化序列b也不c.

因此,如果您memsetMSalters的答案中也使用了无论您做什么,那么在使用它之前,您必须为联盟成员分配一些东西.如果想要保持定义的行为,请不要使用memset.请注意,memset可以安全地使用不是union成员的标准布局对象,因为它们的生命周期开始时为它们获取存储.


总之,为了保持定义的行为,您必须至少初始化一个成员,然后您可以检查与初始化成员共享公共初始化序列的联盟的其他成员.

  1. 如果你的意图是在main函数中使用匿名联合,你可以声明union static:所有静态对象都是零初始化.(但是当你回想起不会发生的功能时,不会重新初始化main()):

    int main(){
     static union {
      s b;
      int a;
      char c;
      };
     //...
     }
    
    Run Code Online (Sandbox Code Playgroud)

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

    如果T是(可能是cv限定的)并集类型,则对象的第一个非静态命名数据成员将进行零初始化,并将填充初始化为零位;

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

    //...
    int main(){
      union {
       int a;
       s b{};
       char c;
       };
      //...
      }
    
    Run Code Online (Sandbox Code Playgroud)

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

    union的大小足以包含其最大的非静态数据成员.每个非静态数据成员都被分配,就好像它是结构的唯一成员一样.[注意:union对象及其非静态数据成员是指针可互换的(3.9.2,5.2.9).因此,union对象的所有非静态数据成员都具有相同的地址.

  3. 如果S内部有填充,那么只需为初始化目的声明一个char数组:

    //...
    int main(){
      union {
       char _initialization[sizeof(s)]{};
       int a;
       s b;
       char c;
       };
      //...
      }
    
    Run Code Online (Sandbox Code Playgroud)

注意:使用您的示例或最后两个代码示例,使用的代码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)
Run Code Online (Sandbox Code Playgroud)

  • @AbhinavGauniyal作为一般规则,当你在C++代码中看到memset或memcopy等时......即使对于琐碎的类型,请记住它们是一个C++样式代码,至少与这些c-family函数一样有效.请记住,已经开发出C++标准的人已经考虑过用这些旧的c-family函数替换它们.例如,许多论文已经证明std :: copy比memcpy更有效,或者std :: sort比qsort更有效...... (3认同)