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

时间:2017-02-21 07:59:23

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

我应该如何清除匿名联盟?我在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()

此外,如果这样可行,我应该在使用$ h=$'Fruit\tColor' $ t=$'Total\tTotal' $ cat file Mango Yellow Lemon Green $ awk -v h="$h" -v t="$t" 'BEGIN{FS=" ";OFS="\t";print h}{print $1,$2}END{print t}' file Fruit Color Mango Yellow Lemon Green Total Total 激活最大会员之前取消注释(1)吗?

3 个答案:

答案 0 :(得分:5)

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

  

...   除非对象是联盟成员或其子对象,否则其生命周期仅在该联盟成员开始时生效   是联合中的初始化成员(8.6.1,12.6.2),或者如9.3中所述。对象的生命周期   类型T结束时:   (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

如果您使用{em> MSalters 的答案中提出的memset ,无论您做什么,您都必须为成员分配内容在使用它之前的联盟。如果要保留已定义的行为,请不要使用memset。请注意,memset可以安全地与标准布局对象一起使用,这些对象不是union的成员,因为它们的生命周期开始时为它们获取存储。

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

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

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

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

      

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

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

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

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

      

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

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

    //...
    int main(){
      union {
       char _initialization[sizeof(s)]{};
       int a;
       s b;
       char c;
       };
      //...
      }
    
  4. Nota:使用您的示例或最后两个代码示例,使用memset的代码生成完全相同的初始化指令集(clang - &gt; 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)
    

答案 1 :(得分:4)

每个成员只需memset,并依靠优化器来消除冗余写入。

答案 2 :(得分:1)

我只是分享一个想法,也许我们可以使用这样的元编程:

template<typename T1, typename T2>
struct Bigger
{
  typedef typename std::conditional<sizeof(T1) >= sizeof(T2), T1, T2>::type Type;
};

// Recursion helper
template<typename...>
struct BiggestHelper;

// 2 or more types
template<typename T1, typename T2, typename... TArgs>
struct BiggestHelper<T1, T2, TArgs...>
{
    typedef typename Bigger<T1, typename BiggestHelper<T2, TArgs...>::Type>::Type Type;
};

// Exactly 2 types
template<typename T1, typename T2>
struct BiggestHelper<T1, T2>
{
    typedef typename Bigger<T1, T2>::Type Type;
};

// Exactly one type
template<typename T>
struct BiggestHelper<T>
{
    typedef T Type;
};

template<typename... TArgs>
struct Biggest
{
    typedef typename BiggestHelper<TArgs...>::Type Type;
};

所以在主要功能中,我们可以这样做:

std::memset(&b, 0, sizeof(Biggest<int,s,char>::Type));