【问题标题】:C++ Union Member Access And Undefined BehaviourC++ 联合成员访问和未定义行为
【发布时间】:2019-08-12 09:08:30
【问题描述】:

我目前正在从事一个项目,在该项目中我获得了以下信息 结构。我的工作是 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.- 尾注]

是否在packetIdFlipped = myHeader.wId &lt;&lt; 8 行中读取myHeader.wId 未定义的行为?

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

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

提前致谢

【问题讨论】:

  • 匿名结构在 C++ 中的格式不正确。 IE。这个:struct{ /* members */ };
  • @eerorika 谢谢 - 我什至没有意识到这一点。调查它导致我发表评论 类似于 union,一个未命名的结构成员,其类型是一个没有名称的结构,称为匿名结构。匿名结构的每个成员都被认为是封闭结构或联合的成员。如果封闭的结构或联合也是匿名的,这将递归地应用 here。这是否意味着上面的代码实际上 不是 UB,因为内部结构和联合的每个成员都被视为 struct PacketHeader 的成员?
  • 是UB。匿名的工会不会改变任何事情。
  • 似乎找不到编辑评论按钮,但仔细阅读后,我认为该链接无论如何都是指 C。非常感谢,再次

标签: c++ language-lawyer undefined-behavior unions


【解决方案1】:

函数 BuildPacketHeader(uint8_t) 为成员赋值 wMake 和 wMod 显式,并且不写入成员 wId。我的 问题是关于从里面的成员 wId 阅读的安全性 返回的结构实例。

是的,它是 UB。这并不意味着它不起作用,只是它可能不起作用。您可以在 BuildPacketHeader 中使用 memcpy 来避免这种情况(请参阅 thisthis)。

【讨论】:

  • 感谢您的回复和 memcpy 提示。考虑到内部联合和结构都是匿名的(正如 eerorika 所指出的),这仍然是 UB,并且以下注释 类似于联合,其类型为无名称结构的结构的未命名成员被称为匿名结构.匿名结构的每个成员都被认为是封闭结构或联合的成员。如果封闭的结构或联合也是匿名的,这将递归地应用。source 编辑:无论如何,链接似乎是指 C,我很抱歉
【解决方案2】:

是否在packetIdFlipped = myHeader.wId &lt;&lt; 8 行中读取myHeader.wId 未定义行为?

是的。您分配给 wMakewMod 使 unamed 结构成为活动成员,因此 wId 是非活动成员,如果不为其设置值,则不允许您读取它。

这就是通用初始序列的意思吗?

common initial sequence 是两个standard layout types 以相同的顺序共享相同的成员。在

struct foo
{
    int a;
    int b;
};

struct bar
{
    int a;
    int b;
    int c;
};

abfoobar 中属于同一类型,因此它们是它们共同的初始序列。如果您将foobar 的对象放在一个联合中,那么在将其设置在其中一个对象中之后,从枯萎对象中读取ab 是安全的。

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

【讨论】:

  • wId 包装在struct ID{ ... } 中是否足以提供标准布局类型?并且,如果是这样,它是否会与以下匿名结构共享一个公共初始序列(在提供的示例中 - 现在将更改,因为 eerorika 指出它定义不正确)结构,因为每个结构都包含一个uint16_t 初始成员?或者后一个结构的成员是位字段这一事实是否意味着它们不是常见的类型?
  • 在问更多问题之前,我应该更彻底地阅读,我很抱歉。您发布的链接链接到另一个页面,说明 如果它们是位域,它们具有相同的宽度
  • @cprlkleg 那仍然是不合法的。正如您所发现的,这两个结构都必须有一个位字段才能合法。
【解决方案3】:

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++ 说它是未定义的。

最后,从实用的角度来看,如果你不搞乱填充并获得标准布局,编译器通常会做正确的事情,因为这是 C 中相当古老的使用模式,打破它会破坏很多代码.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-23
    • 1970-01-01
    • 1970-01-01
    • 2012-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多