我目前正在一个项目中,向我提供以下内容 结构。我的工作是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;
根据结构实例的使用方式,所需的字节序
结构可以是大端或小端。作为前两个字节
结构每个都是单个字节,这些字节的字节序不需要更改
变化。
存储为单个uint16_t
的字节2和3是我们需要的唯一字节
交换以达到所需的字节序。为了实现字节序交换,我们有
一直在执行以下操作:
//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;
函数BuildPacketHeader(uint8_t)
将值分配给成员wMake
和
明确wMod
,并且不写入成员wId
。我的问题是关于
在返回的实例中读取成员wId
的安全性
结构。
诸如的问题 Accessing inactive union member and undefined behavior?, Purpose of Unions in C and C++, 和Section 10.4 of the draft standard I have都提到了由于访问C ++中的联合的不活动成员而引起的未定义行为。
链接的草案的10.4节中的第1段也包含以下注释,尽管我不确定我是否了解所使用的所有术语:
[注意:为了简化并集的使用,做出了一项特殊保证:如果标准布局并集包含多个共享公共初始序列(10.3)的标准布局结构,并且如果非静态数据成员为此标准布局联合类型的对象是活动的,并且是标准布局结构之一,允许检查任何标准布局结构成员的公共初始序列;参见10.3.—尾注]
是否在myHeader.wId
行中读取packetIdFlipped = myHeader.wId << 8
的未定义行为?
未命名结构是否是活动成员,因为它是函数调用中最后写入的成员?
还是该注释表示访问wId
成员是安全的,因为它和结构共享一个公共类型? (这是常见初始序列的意思吗?)
预先感谢
答案 0 :(得分:4)
函数BuildPacketHeader(uint8_t)将值分配给成员 wMake和wMod显式,并且不写入成员wId。我的 问题是关于从内部成员wId读取数据的安全性 返回的结构实例。
是的,是UB。这并不意味着它不起作用,只是它可能不起作用。您可以在BuildPacketHeader中使用memcpy来避免这种情况(请参阅this和this)。
答案 1 :(得分:1)
是否在
myHeader.wId
行中读取packetIdFlipped = myHeader.wId << 8
的未定义行为?
是的。您已分配给wMake
和wMod
来使未修饰的结构成为活动成员,因此wId
是非活动成员,并且如果不对其设置值,则不允许您读取它。
这是常见初始序列的意思吗?
common initial sequence是两个standard layout types以相同顺序共享相同成员的时候。在
struct foo
{
int a;
int b;
};
struct bar
{
int a;
int b;
int c;
};
a
和b
在foo
和bar
中具有相同的类型,因此它们是它们的共同初始序列。如果将foo
和bar
的对象放在一个并集中,则将其中的一个设置为凋零对象后,可以安全地读取a
或b
。>
这不是您的情况,因为wId
不是标准的布局类型结构。
答案 2 :(得分:1)
C ++标准类型说的是给定的两个结构A和B以及以下内容:
private ShipmentRepository shipmentRepository;
以下是有效代码:
public void add(Shipment shipment)
您读写相同的类型。这显然是正确的代码。
但是,当您使用以下代码时,就会出现问题:
union U
{
A a;
B b;
};
我们对A和B有什么了解?首先,它们共享相同的内存空间。现在,C ++标准已将其明确声明为未定义的行为。
但这并不意味着它完全不在窗口之内。 C99发挥了作用,因为它是规范基础,并且对工会的保证很弱。也就是说,如果联合成员具有相同的内存布局,则它们是兼容的,并且每个结构的第一个内存地址都相同。因此,即使您可以确保以正确的方式填充所有struct / union成员,也可以保证操作安全,即使C ++表示未定义。
最后,从务实的角度来看,如果您不喜欢填充并获得标准布局,则编译器通常会做正确的事,因为这是C语言中的一种很老的用法模式,破坏它会破坏很多代码