C++ C++;联盟成员访问和未定义的行为
我目前正在从事一个项目,在该项目中,我获得了以下信息: 结构。我的工作是C++,但是项目同时使用C和C++。相同的结构 C和C++都使用定义。C++ C++;联盟成员访问和未定义的行为,c++,language-lawyer,undefined-behavior,unions,C++,Language Lawyer,Undefined Behavior,Unions,我目前正在从事一个项目,在该项目中,我获得了以下信息: 结构。我的工作是C++,但是项目同时使用C和C++。相同的结构 C和C++都使用定义。 typedef struct PacketHeader { //Byte 0 uint8_t bRes :4; uint8_t bEmpty :1; uint8_t bWait
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;
根据结构实例的使用方式,所需的
结构可以是大端或小端。作为
结构是每个单个字节,当endianness
变化。
字节2和3存储为单个uint16\t
,是我们需要保存的唯一字节
交换以获得所需的端点。要实现endianness交换,我们必须
我们一直在执行以下任务:
//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;
//返回PacketHeader的构造实例,并设置相关字段和提供的计数器值
PacketHeader myHeader=mmt::BuildPacketHeader(计数);
uint16_t包装为唇形;
//字节2和3的交换位置
packetIdFlipped=myHeader.wId>8;
myHeader.wId=packetIdFlipped;
函数BuildPacketHeader(uint8\u t)
为成员wMake
和
wMod
显式,并且不向成员wId
写入。我的问题是关于
从返回的
结构
诸如
,
,
并且每个都提到了在C++中访问联盟中的非活动成员所产生的未定义行为。
链接草案第10.4节第1段也包含以下注释,尽管我不确定我是否理解所使用的所有术语:
[注:为简化联合体的使用,我们提供了一项特殊保证:如果一个标准布局联合体包含多个共享公共初始序列的标准布局结构(10.3),并且如果此标准布局联合类型的对象的非静态数据成员处于活动状态并且是标准布局结构之一,则允许检查任何标准布局结构成员的公共初始序列;请参见10.3.-结束注释]
正在读取行packetIdFlipped=myHeader.wId中的myHeader.wId
正在读取行packetIdFlipped=myHeader.wId中的myHeader.wId
函数BuildPacketHeader(uint8_t)为成员分配值
wMake和wMod显式,不向成员wId写入。我的
问题是关于从wId内部成员处读取数据的安全性
返回的结构实例
是的,是UB。这并不意味着它不起作用,只是它可能不起作用。您可以在BuffDePosiKeDead中使用MeMyPy来避免(参见和).< /P> < P>什么是C++标准类型,给出了两个结构A和B,以及下面的声明:
union U
{
A a;
B b;
};
以下是有效代码:
U u;
A a;
u.a = a;
a = u.a;
B b;
u.b = b;
b = u.b;
你读和写的是同一种类型。这显然是正确的代码
但当您有以下代码时,问题就会出现:
A a;
B b;
u.a = a;
b = u.b;
我们对A和B了解多少?首先,在联盟中,它们共享相同的内存空间。现在C++标准已经明确地声明它是未定义的行为。
但这并不意味着它完全在窗外。C99发挥了作用,因为它是规范性基础,对工会的保障也很薄弱。也就是说,如果联合成员具有相同的内存布局,那么它们是兼容的,并且每个结构的第一个内存地址都是相同的。因此,如果您可以确保结构/联盟成员都以正确的方式填充,即使C++表示未定义,操作也安全。p>
最后,从语用的角度来看,如果你不填充填充,得到标准布局,编译器通常会做正确的事情,因为这是C++中一个非常旧的使用模式,并且打破它会破坏很多代码。即:struct{/*members*/}代码>@eerorika谢谢-我还没意识到。通过查看它,我得到了一条类似于union的评论,一个结构的未命名成员,其类型是一个没有名字的结构,称为匿名结构。匿名结构的每个成员都被视为封闭结构或联合的成员。如果封闭结构或并集也是匿名的,则递归地应用此方法。这是否意味着上面的代码实际上不是UB,因为内部结构和union的每个成员都被视为struct PacketHeader
的成员?它是UB。匿名工会并没有改变任何事情。似乎找不到“编辑评论”按钮,但仔细阅读,我认为该链接无论如何都是指C。再次感谢您的回复和提示。考虑到内部union和struct都是匿名的(正如eerorika所指出的),下面的注释类似于union,结构的未命名成员(其类型是没有名称的结构)称为匿名结构。匿名结构的每个成员都被视为封闭结构或联合的成员。如果封闭的结构或联合也是匿名的,则递归地应用此方法。编辑:链接似乎是指C无论如何,我的道歉是在struct ID{…}
中包装wId
是否足以提供标准布局类型?如果是这样的话,它是否会与以下匿名(在提供的示例中,由于eerorika指出它定义不清,所以将被更改)结构共享一个共同的初始序列,因为每个结构都包含一个uint16\u t
初始成员?或者,后一个结构的成员是一个位字段这一事实是否意味着它们不是普通类型?在提出更多问题之前,我应该仔细阅读,抱歉。您发布的链接链接到了另一个页面,澄清了这一点,如果它们是位字段,则它们的宽度相同。@cprlkleg,这仍然是不合法的。正如您所发现的,两个结构都必须有一个位字段才能合法。
A a;
B b;
u.a = a;
b = u.b;