Luc*_*ore 114 c++ undefined-behavior unions language-lawyer
我的印象是访问union除最后一个成员之外的成员是UB,但我似乎无法找到一个可靠的参考(除了声称它是UB但没有标准支持的答案).
那么,这是不确定的行为?
eca*_*mur 122
令人困惑的是,C明确允许通过联合进行类型惩罚,而C++(c ++ 11)没有这样的权限.
6.5.2.3结构和工会成员
95)如果用于读取union对象内容的成员与上次用于在对象中存储值的成员不同,则该值的对象表示的适当部分将被重新解释为新对象表示如6.2.6所述的类型(有时称为''punning''的过程).这可能是陷阱表示.
C++的情况:
9.5工会[class.union]
在并集中,至多一个非静态数据成员可以在任何时间处于活动状态,也就是说,任何时候最多一个非静态数据成员的值都可以存储在并集中.
C++后来的语言允许使用包含struct具有共同初始序列的s 的联合; 然而,这并不允许打字.
为了确定C++中是否允许使用union type-punning ,我们必须进一步搜索.回想一下,c99是C++ 11的规范性引用(C99与C11具有类似的语言,允许联合类型 - 惩罚):
3.9类型[basic.types]
4 - 类型T的对象的对象表示是由类型T的对象占据的N个无符号字符对象的序列,其中N等于sizeof(T).对象的值表示是保存类型T的值的位集.对于简单的可复制类型,值表示是对象表示中的一组位,用于确定值,该值是实现的一个离散元素 - 定义一组值.42
42)意图是C++的内存模型与ISO/IEC 9899编程语言C的内存模型兼容.
当我们阅读时,它变得特别有趣
3.8对象寿命[basic.life]
类型T的对象的生命周期开始于: - 获得具有适当对齐和类型T大小的存储,并且 - 如果对象具有非平凡初始化,则其初始化完成.
因此,对于联合中包含的原始类型(事实上具有简单的初始化),对象的生命周期至少包含联合体本身的生命周期.这允许我们调用
3.9.2复合类型[basic.compound]
如果类型T的对象位于地址A,则类型为cv T*的指针(其值为地址A)被称为指向该对象,而不管该值是如何获得的.
假设我们感兴趣的操作是类型惩罚,即获取非活动联合成员的值,并且根据上面给出的我们对该成员引用的对象有有效引用,该操作是lvalue-to -rvalue转换:
4.1左值到右值的转换[conv.lval]
可以将非函数非数组类型的glvalue
T转换为prvalue.如果T是不完整类型,则需要进行此转换的程序格式不正确.如果glvalue引用的对象不是类型的对象,T并且不是派生类型T的对象,或者如果对象未初始化,则需要此转换的程序具有未定义的行为.
那么问题是作为非活动联合成员的对象是否通过存储初始化为活动联合成员.据我所知,情况并非如此,所以尽管如此:
char数组存储中并返回(3.9:2),或定义了非活动成员对联合的访问,并定义为遵循对象和值表示,没有上述插入之一的访问是未定义的行为.这对于允许在这样的程序上执行的优化具有影响,因为实现当然可以假设未发生未定义的行为.
也就是说,虽然我们可以合法地形成一个非活动联合成员的左值(这就是为什么在没有构造的情况下分配给非活动成员的原因),但它被认为是未初始化的.
Bo *_*son 26
C++ 11标准就是这样说的
9.5工会
在并集中,至多一个非静态数据成员可以在任何时间处于活动状态,也就是说,任何时候最多一个非静态数据成员的值都可以存储在并集中.
如果只存储一个值,您如何阅读另一个值?它只是不存在.
gcc文档在Implementation defined行为下列出了这一点
- 使用不同类型的成员(C90 6.3.2.3)访问union对象的成员.
对象表示的相关字节被视为用于访问的类型的对象.请参阅类型惩罚.这可能是陷阱表示.
表明C标准不要求这样做.
2016-01-05:通过评论,我与C99缺陷报告#283相关联,该报告在C标准文档中添加了类似的文本作为脚注:
78a)如果用于访问union对象内容的成员与上次用于在对象中存储值的成员不同,则值的对象表示的适当部分将被重新解释为新对象表示按6.2.6中描述的类型(有时称为"类型双关"的过程).这可能是陷阱表示.
虽然脚注不是标准的规范,但不确定它是否澄清了很多.
Jer*_*fin 18
我认为最接近标准的是它的未定义行为,它定义了包含公共初始序列的联合的行为(C99,§6.5.2.3/ 5):
为了简化联合的使用,我们做了一个特殊的保证:如果一个联合包含几个共享一个共同初始序列的结构(见下文),并且如果联合对象当前包含这些结构中的一个,则允许检查公共其中任何一个的初始部分都可以看到完整类型的联合声明.如果对应的成员具有一个或多个初始成员的序列的兼容类型(并且对于位字段,具有相同的宽度),则两个结构共享共同的初始序列.
C++ 11在§9.2/ 19中给出了类似的要求/许可:
如果标准布局联合包含两个或多个共享公共初始序列的标准布局结构,并且如果标准布局联合对象当前包含这些标准布局结构中的一个,则允许检查任何公共初始部分.他们 如果相应的成员具有布局兼容类型并且两个成员都不是位字段,或者两者都是具有相同宽度的位字段(对于一个或多个初始成员的序列),则两个标准布局结构共享共同的初始序列.
虽然两者都没有直接说明,但这两者都具有强烈的含义,即"检查"(阅读)成员只有在1)它是(最近一部分)成员(的一部分),或者2)是共同初始的一部分时才被"允许".序列.
这不是一个直接声明,否则是未定义的行为,但它是我所知道的最接近的行为.
mpu*_*mpu 12
可用答案尚未提及的内容是第6.2.5节第21段的脚注37:
请注意,聚合类型不包含联合类型,因为具有联合类型的对象一次只能包含一个成员.
这个要求似乎明显暗示你不得写成员并阅读另一个成员.在这种情况下,由于缺乏规范,它可能是未定义的行为.
| 归档时间: |
|
| 查看次数: |
15714 次 |
| 最近记录: |