在评论这个答案据说,这将是不确定的行为,以使用工会像如下整数分成的字节。 在那个地方给出的代码相似但不相同这一点,请给一个音符,如果有我改变了代码的未定义行为相关的方面。
union addr {
uint8_t addr8[4];
uint32_t addr32;
};
到现在为止我认为这将是一个精细的方法做事情喜欢addr = {127, 0, 0, 1};
并获得相应的uint32_t
回报。 (我承认这可能会因我的系统的字节序产生不同的结果。然而,问题仍然存在。)
这是不确定的行为? 如果是这样,为什么? (我不知道是什么意思有什么UB在C ++是访问不活跃工会会员。)
C99
- C99是apparantly在这一点上非常接近C ++ 03。
C ++ 03
- 在联合中,数据成员的至多一个可以在任何时间是活动的,即,数据成员的至多一个的值可以在任何时间被存储在联合。 C ++ 03,第9.5节(1),第162页
然而
- 如果POD联合包含几个POD-结构共享一个公共初始序列[...]则允许检查任何POD结构的成员同上公共初始序列 。
- 两个POD结构[...]类型是布局兼容如果它们具有相同数量的非静态数据成员,并且对应的非静态数据成员(按顺序)具有布局兼容的类型 C ++ 03,第9.2节(14), 157页
- 如果两种类型的T1和T2是相同的类型,则T1和T2是布局兼容的类型。 C ++ 03,第3.9节(11),第53页
结论
- 如
uint8_t[4]
和uint32_t
是不是同一类型(我想,一个严格别名东西 )(加二者不是POD-结构/联合)以上确实UB?
C ++ 11
- 需要注意的是聚合类型不包括联合类型,因为与工会类型的对象只能包含一次一个成员。 C ++ 11,脚注46,第42页
我不知道是什么意思有什么UB在C ++是访问不活动的工会会员。
基本上,它的意思是,你可以从工会阅读,而不必调用未定义行为的唯一成员是最后写了一。 换句话说,如果你写addr32
,你只能读取addr32
,不addr8
反之亦然。
一个例子也可以在这里 。
编辑:既然已经有很多的讨论,如果这是UB与否,考虑下面的(完全有效)C ++ 11例;
union olle {
std::string str;
std::wstring wstr;
};
在这里你绝对可以看到,激活海峡和阅读WSTR可能是一个问题。 你可以认为这是一个极端的例子,因为你甚至有做一个放置新激活的成员,但该规范实际上涵盖这种情况下没有提到它的被认为是在与活动成员的其他方式的一个特例。
[编辑:阅读下面我编辑的部分,因为我现在不能确定这是否是未定义的行为或不; 我会离开我的大部分答案相同的,然而,直到我可以确认进一步]是的,这是不确定的行为。 C ++标准,9.5.1节,规定:
在联合中,非静态数据成员的至多一个可以在任何时间是活动的,即,非静态数据成员的至多一个的值可以在任何时间被存储在联合。 [注:一个特殊的保障是为了简化使用工会提出:如果标准布局工会包含共享一个公共初始序列(9.2)几个标准布局结构,如果这个标准布局工会类型的对象包含标准的布局结构中的一个,它允许检查的任何标准布局结构成员的公共初始序列; 见9.2。 - 注完]
这意味着,只有最近写入成员可以有效地从以及读取(从别人读书是技术上未定义的行为)。 只有联盟的一个成员可以在任何时候活跃。 不是两个。
你可能会问,为什么? 考虑你的榜样。 C ++不强制的字节序addr32
。 这可能是大端,小端或中端。 如果你写addr8
,然后读取addr32
,C ++不能保证你会得到正确的值,因为在这种情况下,字节序。 其中一台电脑,它可能是一个值,并在另一个,它可能是一个不同的值。 因此,这样做的(即,写入到一个构件和读取的不同的一个)是未定义的行为。
编辑:对于那些想知道什么是“主动”的方式, 对工会MSDN文档指出:
工会的积极成员,是其价值被最近设置的,只有那件有一个有效的价值之一。
编辑编辑:我一直认为这样的行为是不确定的,但现在我不后R.费尔南德斯Martinho的意见和答案,并重新读取引自MSDN经过这么肯定。 该值肯定是不确定/不确定的,但现在我不那么肯定,如果该行为(未定义的值意味着你可能会得到不同的结果反馈,不确定的行为意味着你的系统可能会崩溃,这两者是不同的东西)。 我要这进一步考虑和与他人我知道看谈话,如果我能找到一个更明确的答案。
我不认为这是肯定地说,然而,在一般的读书(当然除了在标准中特别说明,)在工会的非活动成员可以是不确定的行为,但我不知道这是否总是 (即有可能会超越特殊说明在C ++标准的部分,我引述)一些例外。
Basically because in C++ you are allowed to access just the active member of an union.
This means that if you set addr8
then you should access just that one until you set addr32
, so that you can access it and so on. Setting one member to access data from another one is what should cause undefined behavior.
A member is considered active when you set it, and it remains so until another one becomes the active one.
坦率地说,我找不到在标准没有提到,这样做是不确定的行为。 该标准确实定义“活跃会员”为工会的概念,但它似乎并没有使用该想法什么比解释如何更改活动成员(§9.5p4),并定义常量表达式等(§5.9p2 )。 具体地说,它似乎并没有使访问无论是主动还是非活跃会员的有效期明确提及。
据我所看到的,像下面这样可能会导致一个严格别名侵犯,这是不确定的行为:
union example0 {
short some_other_view[sizeof(double)/sizeof(short)];
double value;
};
这不会导致严格别名侵犯,因为工会一些特殊的规则。 如果您访问使用,不能混淆类型,即“正常”严格别名违反了相同的内存位置它发生。
但是 ,因为有一个例外char
,当谈到重叠规则,下面不会导致同种违法行为:
union example1 {
char byte_view[sizeof(double)];
double value;
};
据我所看到的,没有什么留下具有未定义操作下面的代码标准:
example1 e;
e.value = 10.0;
std::out << e.byte_view[0];