c++ - 使用 union 在具有共同初始序列的两个结构之间进行转换是否合法且定义明确的行为(参见示例)?

标签 c++ c c99 unions c89

我有一个带有公开面向结构 A 和内部结构 B 的 API,并且需要能够将结构 B 转换为结构 A。以下代码是否合法和明确定义的行为 C99(和 VS 2010/C89)和 C++03/C++11?如果是,请解释是什么使它定义明确。如果不是,那么在两个结构之间进行转换的最有效和跨平台的方法是什么?

struct A {
  uint32_t x;
  uint32_t y;
  uint32_t z;
};

struct B {
  uint32_t x;
  uint32_t y;
  uint32_t z;
  uint64_t c;
};

union U {
  struct A a;
  struct B b;
};

int main(int argc, char* argv[]) {
  U u;
  u.b.x = 1;
  u.b.y = 2;
  u.b.z = 3;
  u.b.c = 64;

  /* Is it legal and well defined behavior when accessing the non-write member of a union in this case? */
  DoSomething(u.a.x, u.a.y, u.a.z);

  return 0;
}


更新

我简化了示例并编写了两个不同的应用程序。一个基于 memcpy,另一个使用 union 。


union :

struct A {
  int x;
  int y;
  int z;
};

struct B {
  int x;
  int y;
  int z;
  long c;
};

union U {
  struct A a;
  struct B b;
};

int main(int argc, char* argv[]) {
  U u;
  u.b.x = 1;
  u.b.y = 2;
  u.b.z = 3;
  u.b.c = 64;
  const A* a = &u.a;
  return 0;
}


memcpy:

#include <string.h>

struct A {
  int x;
  int y;
  int z;
};

struct B {
  int x;
  int y;
  int z;
  long c;
};

int main(int argc, char* argv[]) {
  B b;
  b.x = 1;
  b.y = 2;
  b.z = 3;
  b.c = 64;
  A a;
  memcpy(&a, &b, sizeof(a));
  return 0;
}



Profiled Assembly [DEBUG](Xcode 6.4,默认 C++ 编译器):

这是 Debug模式的程序集的相关差异。当我分析发布版本时,程序集没有任何差异。


union :

movq     %rcx, -48(%rbp)


内存:

movq    -40(%rbp), %rsi
movq    %rsi, -56(%rbp)
movl    -32(%rbp), %edi
movl    %edi, -48(%rbp)



警告:

基于 union 的示例代码会产生关于变量“a”未使用的警告。由于分析的程序集来自调试,我不知道是否有任何影响。

最佳答案

这很好,因为您正在访问的成员是公共(public)初始序列的元素

C11(6.5.2.3 结构和 union 成员语义):

[...] if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible. Two structures share a common initial sequence if corresponding members have compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

C++03 ([class.mem]/16):

If a POD-union contains two or more POD-structs that share a common initial sequence, and if the POD-union object currently contains one of these POD-structs, it is permitted to inspect the common initial part of any of them. Two POD-structs share a common initial sequence if corresponding members have layout-compatible types (and, for bit-fields, the same widths) for a sequence of one or more initial members.

这两个标准的其他版本有类似的语言;从 C++11 开始,使用的术语是 standard-layout 而不是 POD


我认为混淆可能是因为 C 允许 type-punning(为不同类型的成员命名)通过 union 而 C++ 不允许;这是确保 C/C++ 兼容性的主要情况,您必须使用 memcpy。但是在您的情况下,您正在访问的元素具有 same 类型并且前面是兼容类型的成员,因此类型双关规则不相关。

关于c++ - 使用 union 在具有共同初始序列的两个结构之间进行转换是否合法且定义明确的行为(参见示例)?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31553409/

相关文章:

c++ - Q : Generate argument list at compile time

c++ - 关于C++运算符优先级的问题

c++ - 删除和移动数组 C++ 中的剩余元素

c - 代码 "should"有效,但程序停止工作

c - 使用散点图发送 2 个昏暗数组

c++ - NULL 函数指针

c++ - QScrollArea::ensureVisible() 和 QScrollArea::setWidget()

c - 制作简单的linux内核模块

c - C 编译器如何知道数组的大小?

c - 初始化数组/结构的不熟悉语法,寻找解释