用嵌套结构初始化联合

我将C99代码移植到C ++(14或17),并且在许多地方都使用了列表初始化程序。现在,我遇到了编译错误,并且想知道初始化结构嵌套的联合的最简单方法。例如,下面的C代码片段可以正常工作:

#include <stdint.h>

typedef union Word_t
{
    uint32_t word32Bits;
    struct
    {
        uint16_t leastSignificant16Bits;
        uint16_t mostSignificant16Bits;
    };
} Word_t;

int main()
{
    Word_t w1 = (Word_t) {.word32Bits = 0x1234ABCD};
    printf("%x\n",w1.word32Bits);

    Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234,.leastSignificant16Bits = 0xABCD};
    printf("%x\n",w2.word32Bits);

    return 0;
}


$ gcc test.c --std=c99 -o a && ./a
1234abcd
1234abcd

但是,在C ++中无法编译:

#include <stdint.h>

typedef union Word_t
{
    uint32_t word32Bits;
    struct
    {
        uint16_t leastSignificant16Bits;
        uint16_t mostSignificant16Bits;
    } _word16Bits;
} Word_t;

int main()
{
    Word_t w1 = (Word_t) {.word32Bits = 0x1234ABCD};
    printf("%x\n",w2.word32Bits);

    return 0;
}


```bash
$ g++ test.c --std=c++14 -o a && ./a
test.c: In function ‘int main()’:
test.c:57:92: error: ‘Word_t’ has no non-static data member named ‘mostSignificant16Bits’
     Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234,.leastSignificant16Bits = 0xABCD};

我发现的工作量解决方案是将其初始化为零,然后按如下所示设置结构的内部值:


int main()
{
    Word_t w1 = (Word_t) {.word32Bits = 0x1234ABCD};
    printf("%x\n",w1.word32Bits);

    Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234,.leastSignificant16Bits = 0xABCD};


    Word_t w2 = {0};
    w2._word16Bits = {0x1234,0xABCD};

    return 0;
}

这是可行的,但是例如,它不允许我明确说出.mostSignificant16Bits = 0x1234,我认为这很有用,尤其是在阅读代码时。

我尝试了一些事情,例如定义静态成员,创建用户定义的构造函数,但仍然不知道如何简化我要做的重构。理想情况下,我想保留变量声明的原样Word_t w2 = (Word_t) {.mostSignificant16Bits = 0x1234,.leastSignificant16Bits = 0xABCD},而所有更改都在Word_t的定义中完成。

shuang890802 回答:用嵌套结构初始化联合

语法问题

聚集初始化中的

Designated initializers正式是C++20 standard的一部分。

但是与C99相比,它们受到了严重的限制:

  • 它们必须以与声明相同的顺序出现;
  • 所有指定元素必须是集合的直接成员;
  • 只有嵌套初始化器时,嵌套才可以

在您的情况下,将编译以下内容,但它不能提供您期望的显式命名所带来的灵活性:

Word_t w2 = (Word_t) {._word16Bits  { .leastSignificant16Bits = 0xABCD,.mostSignificant16Bits = 0x1234} };

更严重的问题

首先,此代码(如果可行)不是可移植的:它假定目标体系结构为little endianness

第二,这在这里至关重要,C ++对联合有严格的约束。考虑到对象生命周期的一致性,这些是必需的。特别是:

  

[class.union] / 1 :在一个联合中,任意时间最多可以有一个非静态数据成员处于活动状态,即,最多一个值   随时可以将一个非静态数据成员存储在联合中。

因此,如果在一个成员处于活动状态(初始化程序中使用的一个成员)的情况下构造联合,则另一个成员处于非活动状态,因此不应访问它。预见的唯一例外不适用于您的情况:

  

[注意:为了简化使用,做出了一项特殊保证。   联合:如果一个标准版式联合包含多个标准版式   共享共同初始序列的结构,如果对象   此标准布局联合类型的其中一个包含标准布局   结构,可以检查任何常见的初始序列   标准布局的struct成员; —尾注]

该标准还提供了有关更改联盟的活动成员的方式的提示:

  

[注意:通常,必须使用显式析构函数调用,并且   放置新的运算符以更改联盟的活动成员。 - 结束   注意]

对于简单的标量类型来说,它可以像大多数主流编译器一样编译和运行。

但是,重点是,您使用的工会与标准不兼容。它是UB,即使现在可以在某些实现上使用,在每个新的编译器版本中,您也无法保证它会继续运行,从而使您的所有投资面临风险。

为什么是时候改变方法了?

C99对联合的约束比C ++少。但是,对于设置另一个工会成员时在一个工会成员中可能读取的值,它也不提供任何保证:

  

6.2.6.1/7 :当值存储在联合类型的对象的成员中时,该对象表示形式的字节不   对应于该成员,但对应于其他成员   未指定的值。

在附件C99 / J中提到,这种联合使用是未指定的行为,可能会导致可移植性问题。

,

这适用于C和C ++:

#include <stdio.h>
#include <stdint.h>

typedef union Word_t
{
    uint32_t word32Bits;
    struct
    {
        uint16_t leastSignificant16Bits;
        uint16_t mostSignificant16Bits;
    } _word16Bits;
} Word_t;

int main()
{
    Word_t w1 = {.word32Bits = 0x1234ABCD};
    printf("%x\n",w1.word32Bits);

    Word_t w2 = {._word16Bits={.leastSignificant16Bits = 0xABCD,.mostSignificant16Bits = 0x1234}};
    printf("%x\n",w2.word32Bits);

    return 0;
}

编辑:指定的初始化程序需要使用--std=c++2a而不是--std=c++14

$ g++ -Wall -Wextra test.c --std=c++2a -pedantic -pedantic-errors -o a && ./a
1234abcd
1234abcd
$ gcc -Wall -Wextra test.c -pedantic -pedantic-errors -o a && ./a
1234abcd
1234abcd

注意:在C ++中,您必须以与结构声明中相同的顺序指定带标签的初始化程序。

如Ted Lyngmo的评论中所述,在C ++中,写一个工会成员和从另一个工会成员读取是未定义的行为。

在任何情况下,使用并集从较长的数据类型中提取较短的部分或将它们组合都取决于实现。在大端系统中,leastSignificant16BitsmostSignificant16Bits的顺序将颠倒。

本文链接:https://www.f2er.com/3150058.html

大家都在问