C++ 联合成员访问和未定义行为

cpr*_*leg 5 c++ undefined-behavior unions language-lawyer

我目前正在从事一个项目,其中提供了以下结构。我的工作是 C++,但该项目同时使用 C 和 C++。C 和 C++ 都使用相同的结构定义。

typedef struct PacketHeader {
    //Byte 0
    uint8_t  bRes                           :4;
    uint8_t  bEmpty                         :1;
    uint8_t  bWait                          :1;
    uint8_t  bErr                           :1;
    uint8_t  bEnable                        :1;
    //Byte 1
    uint8_t  bInst                          :4;
    uint8_t  bCount                         :3;
    uint8_t  bRres                          :1;
    //Bytes 2, 3
    union {
        uint16_t wId;    /* Needed for Endian swapping */
        struct{
            uint16_t wMake                  :4;
            uint16_t wMod                   :12;
        };
    };
} PacketHeader;
Run Code Online (Sandbox Code Playgroud)

根据结构实例的使用方式,结构所需的字节序可以是大端或小端。由于结构的前两个字节都是单个字节,因此当字节序更改时不需要更改这些字节。字节 2 和 3 存储为单个uint16_t,是我们需要交换以实现所需字节顺序的唯一字节。为了实现字节序交换,我们一直在执行以下操作:

//Returns a constructed instance of PacketHeader with relevant fields set and the provided counter value
PacketHeader myHeader = mmt::BuildPacketHeader(count);

uint16_t packetIdFlipped;
//Swap positions of byte 2 and 3
packetIdFlipped = myHeader.wId << 8;
packetIdFlipped |= (uint16_t)myHeader.wId >> 8;

myHeader.wId = packetIdFlipped;
Run Code Online (Sandbox Code Playgroud)

该函数BuildPacketHeader(uint8_t)将值分配给成员wMakewMod明确,并且写入构件wId。我的问题是关于从wId结构的返回实例内的成员读取的安全性。

诸如 访问非活动联合成员和未定义行为之类的问题?在C和C ++联盟的目的,和标准草案的第10.4节我有每个提从用C访问联合的非活动构件++所引起的不确定的行为。

链接草案第 10.4 节中的第 1 段还包含以下注释,但我不确定我是否理解所有使用的术语:

[注意:为了简化联合的使用,做了一个特殊的保证:如果一个标准布局联合包含多个标准布局结构,这些结构共享一个共同的初始序列 (10.3),并且如果一个对象的非静态数据成员此标准布局联合类型是活动的并且是标准布局结构之一,允许检查任何标准布局结构成员的公共初始序列;见 10.3.-尾注]

myHeader.wId在线阅读是packetIdFlipped = myHeader.wId << 8未定义的行为吗?

未命名的结构是否是活动成员,因为它是函数调用中写入的最后一个成员?

或者注释是否意味着访问wId成员是安全的,因为它和结构共享一个公共类型?(这就是共同初始序列的意思吗?)

提前致谢

Nat*_*ica 1

myHeader.wId在线读取packetIdFlipped = myHeader.wId << 8未定义行为吗?

是的。您分配给未命名的结构wMake并将其设置为活动成员,非活动成员也是如此,并且不允许在未为其设置值的情况下读取它。wModwId

这就是共同初始序列的含义吗?

常见的初始顺序是两个标准布局类型以相同的顺序共享相同的成员。在

struct foo
{
    int a;
    int b;
};

struct bar
{
    int a;
    int b;
    int c;
};
Run Code Online (Sandbox Code Playgroud)

ab属于同一类型foobar因此它们是它们的公共初始序列。如果将foo和 的对象放入联合中,则在将对象设置为其中之一后,bar可以安全地读取a或从凋零对象中读取。b

但这不是你的情况,因为wId它不是标准布局类型结构。