当前位置: 首页 > 知识库问答 >
问题:

访问不活动的工会成员和未定义的行为?

宰父飞翼
2023-03-14

我的印象是,访问除最后一个集合之外的union成员是UB,但我似乎找不到一个可靠的引用(除了声称它是UB但没有任何标准支持的答案)。

那么,它是未定义的行为吗?

共有3个答案

甘骞尧
2023-03-14

我认为标准最接近于说它是未定义的行为的地方是它定义了包含公共初始序列(C99,§6.5.2.3/5)的联合的行为:

为了简化联合的使用,我们提供了一个特殊的保证:如果联合包含多个共享公共初始序列的结构(见下文),并且如果联合对象当前包含其中一个结构,则允许在可以看到联合完整类型声明的任何地方检查其中任何结构的公共初始部分。如果对应的成员具有一个或多个初始成员序列的兼容类型(对于位字段,宽度相同),则两个结构共享一个公共的初始序列。

C 11 在 §9.2/19 中给出了类似的要求/许可:

如果标准布局联合包含两个或多个共享公共初始序列的标准布局结构,并且如果标准布局联合对象当前包含这些标准布局结构之一,则允许检查其中任何一个的公共初始部分。如果相应的成员具有布局兼容类型,并且两个成员都不是位字段,或者对于一个或多个初始成员的序列来说,两个标准布局结构共享公共初始序列。

虽然两者都没有直接说明,但它们都带有强烈的暗示,即“检查”(阅读)成员只有在1)是最近编写的成员的(部分)或2)是共同初始序列的一部分时才“允许”成员。

这不是一个直接的陈述,即不这样做是未定义的行为,但它是我所知道的最接近的行为。

梁渊
2023-03-14

C11标准是这么说的

9.5工会

在一个联合中,任何时候至多有一个非静态数据成员可以是活动的,也就是说,任何时候至多有一个非静态数据成员的值可以存储在一个联合中。

如果只存储一个值,如何读取另一个值?它只是不在那里。

gcc 文档将其列在“实现定义的行为”下

  • 使用不同类型(C906.3.2.3)的成员访问联合对象的成员。

对象的表示形式的相关字节被视为用于访问的类型的对象。请参见类型双关语。这可能是一个陷阱表示。

表示这不是C标准所要求的。

2016-01-05:通过评论,我链接到了C99缺陷报告#283,它添加了一个类似的文本作为C标准文档的脚注:

78a) 如果用于访问联合对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将重新解释为新类型中的对象表示,如 6.2.6 中所述(此过程有时称为“类型双关”)。这可能是一个陷阱表示。

考虑到脚注对标准来说不是规范性的,不确定它是否澄清了很多。

公孙高畅
2023-03-14

令人困惑的是,C明确允许通过联合进行类型双关语,而C(c 11)没有这样的权限。

c11

95)如果用于读取联合对象内容的成员与上次用于在对象中存储值的成员不同,则值的对象表示的适当部分被重新解释为6.2.6中描述的新类型中的对象表示(该过程有时称为“类型双关”)。这可能是一个陷阱表示。

C的情况:

c 11

在一个联合中,任何时候至多有一个非静态数据成员可以是活动的,也就是说,任何时候至多有一个非静态数据成员的值可以存储在一个联合中。

c #以后的语言允许使用包含具有公共初始序列的< code>struct的联合;然而,这并不允许类型双关。

要确定C中是否允许联合类型双关语,我们必须进一步搜索。回想一下,c99是C 11的规范引用(C99与允许联合类型双关语的C11具有类似的语言):

4 -类型T的对象的对象表示是由类型T的对象占用的N个无符号char对象的序列,其中N等于sizeof(T)。对象的值表示是保存类型t的值的一组位。对于普通的可复制类型,值表示是对象表示中确定值的一组位,该值是实现定义的一组值的一个离散元素。< sup>42
42)目的是使C的内存模型与ISO/IEC 9899编程语言C兼容

当我们阅读时,它变得特别有趣

类型T的对象的生存期始于:-获得类型T的具有适当对齐和大小的存储,并且-如果对象具有非平凡的初始化,则其初始化完成。

因此,对于包含在联合中的基元类型(事实上它具有简单的初始化),对象的生存期至少包含联合本身的生存期。这允许我们调用

如果一个T类型的对象位于地址A,那么一个值为地址A的cv T*类型的指针就指向这个对象,而不管这个值是如何获得的。

假设我们感兴趣的操作是类型双关,即取非活动联合成员的值,并且根据上面的内容,我们对该成员引用的对象有有效引用,则该操作是左值到右值的转换:

非函数、非数组类型T的glvalue可以转换为prvalue。如果T是不完整的类型,则需要这种转换的程序格式错误。如果glvalue引用的对象不是T类型的对象,也不是从T派生的类型的对象,或者如果对象未初始化,则需要这种转换的程序具有未定义的行为。

那么问题来了,作为非活动联合成员的对象是否通过存储初始化为活动联合成员。据我所知,情况并非如此,因此,如果:

  • 将联合复制到char数组存储并返回(3.9:2),或
  • 将一个联合按字节复制到另一个相同类型的联合(3.9:3),或
  • 符合ISO/IEC 9899(就定义而言)的程序元素跨语言边界访问联合(3.9:4注42),然后

非活动成员对联合的访问被定义,并被定义为遵循对象和值表示,没有上述中间点之一的访问是未定义的行为。这对允许在这样的程序上执行的优化有影响,因为实现当然可以假设未定义的行为不会发生。

也就是说,尽管我们可以合法地对非活动联盟成员形成一个左值(这就是为什么在没有构造的情况下分配给非活动成员是可以的),但它被认为是未初始化的。

 类似资料:
  • 例如,我们希望通过索引访问C结构的成员。可以使用联合体方便地实现: 不幸的是,从不是最近写入的联合成员中读取是未定义的行为。也就是说,我还没有发现任何编译器或平台不能像预期的那样工作。你能告诉我称之为未定义行为而不是将其标准化的基本原理是什么吗?

  • 我在使用discord。js的机器人制作,我一直得到这个错误,当我试图使一个音乐机器人??我不知道如何定义会员。它甚至不会加入频道或播放音乐,唯一有效的功能是告诉机器人需要一个链接才能继续

  • 问题内容: 如何获得服务器上所有活动的PHP会话的列表并从一个用户实例中访问它们? 激励的情况是显示站点上所有当前活动用户的列表,用户名存储在每个用户的PHP会话中。 注意:我知道我可以通过数据库(甚至文件系统)创建自己的状态,但是我正在寻找一种利用内置PHP会话机制的方法。 问题答案: 看到响应,尽管有可能,但这并不意味着您 应该 这样做。会话的存储格式没有记录,并且可能随时更改(即使在次要版本

  • 问题内容: 有什么方法可以使“私有”变量(在构造函数中定义的变量)可供原型定义的方法使用? 这有效: 但这不是: 我习惯于在构造函数中定义方法,但出于以下两个原因而逐渐远离它。 问题答案: 不,没有办法。这基本上是相反的范围。 在构造函数内部定义的方法可以访问私有变量,因为所有函数都可以访问定义它们的作用域。 在原型上定义的方法未在构造函数的范围内定义,并且将无法访问构造函数的局部变量。 您仍然可

  • 使用符合03标准的编译器(gcc-3.3.2的安全关键变体)。该标准规定必须定义静态成员对象(9.4.2(4))。它还规定“一个定义”规则适用,但不需要诊断(9.4.2(5))。以下代码有效吗? 也就是说,没有“静态常量int fred::JOE;”。我这样问是因为我们有一个例子(显然),模板类中的静态常量从未定义过,代码在某些上下文中工作,但在其他上下文中不工作。我用一个枚举替换了静态常量int