重要澄清:一些评论者似乎认为我在从一个联合体中复制。请注意 memcpy
,它是从一个普通的 uint32_t
的地址复制的,而不是从联合体中包含的变量。另外,我是通过 memcpy
复制到联合体的一个特定成员 (u.a16
或 &u.x_in_a_union
),而不是直接复制整个联合体本身 (&u
)。
C++ 在联合体方面非常严格 - 只有在最后写入的成员是当前活动成员时,才应从该成员读取:
9.5 联合体 [class.union] [[c++11]] 在联合体中,最多只能有一个非静态数据成员处于活动状态,也就是说,最多只能将一个非静态数据成员的值存储在联合体中。
(当然,编译器不会跟踪哪个成员是活动的。开发人员需要自己确保跟踪)
更新:以下代码块是主要问题,直接反映了问题标题中的文本。如果这段代码没问题,我还有一个关于其他类型的后续问题,但我现在意识到这个第一个代码块本身就很有趣。
#include <cstdint>
uint32_t x = 0x12345678;
union {
double whatever;
uint32_t x_in_a_union; // same type as x
} u;
u.whatever = 3.14;
u.x_in_a_union = x; // surely this is OK, despite involving the inactive member?
std::cout << u.x_in_a_union;
u.whatever = 3.14; // make the double 'active' again
memcpy(&u.x_in_a_union, &x); // same types, so should be OK?
std::cout << u.x_in_a_union; // OK here? What's the active member?
很可能在评论和答案中,紧接着上面这个代码块的部分是主要问题。事后看来,在这个问题中我不需要混合类型!基本上,假设类型相同,u.a = b
与 memcpy(&u.a,&b, sizeof(b))
是一样的吗?
首先,一个相对简单的memcpy
函数让我们可以将一个uint32_t
按照uint16_t
数组读取:
#include <cstdint> # to ensure we have standard versions of these two types
uint32_t x = 0x12345678;
uint16_t a16[2];
static_assert(sizeof(x) == sizeof(a16), "");
std:: memcpy(a16, &x, sizeof(x));
具体行为取决于您平台的字节序,并且您必须小心陷阱表示等问题。但是通常认为(我认为?欢迎反馈!),在正确的上下文和平台上避免有问题的值,上述代码可以完全符合标准。
(如果您对上面的代码有任何问题,请在评论或编辑问题时指出。 在继续下面的“有趣”代码之前,我想确保我们有一个非争议性的版本。)
只有当上述两个代码块都不是未定义行为时,我才想要将它们组合如下:
uint32_t x = 0x12345678;
union {
double whatever;
uint16_t a16[2];
} u;
u.whatever = 3.14; // sets the 'active' member
static_assert(sizeof(u.a16) == sizeof(x)); //any other checks I should do?
std:: memcpy(u.a16, &x, sizeof(x));
// what is the 'active member' of u now, after the memcpy?
cout << u.a16[0] << ' ' << u.a16[1] << endl; // i.e. is this OK?
在这个联合体中,u.whatever
和u.a16
中哪一个是“活动成员”?
最后,我自己的猜测是,我们关心这个问题的原因在于优化编译器可能无法注意到memcpy
发生的事实,从而对哪个成员是活动成员以及哪些数据类型是“活动”的做出错误的假设(但标准允许),因此导致关于别名的错误。编译器可能会以奇怪的方式重新排序memcpy
。这是否是我们关心这个问题的适当总结?
memcpy
复制一个联合,因为可能会读取未初始化的内存。请参见https://dev59.com/O1wX5IYBdhLWcg3wyB_T,尽管这是在C标签上。 - Bathsheba=
(在某些情况下)。memcpy
不能胜任。另一方面,可以说它重用了存储并结束了double
的生命周期,在这种情况下,您将拥有一个没有活动成员的联合体。 - T.C.