【问题标题】:How is a union different from a struct? Do other languages have similar constructs? [duplicate]联合与结构有何不同?其他语言有类似的结构吗? [复制]
【发布时间】:2010-12-17 02:20:33
【问题描述】:

可能重复:
Difference between a Structure and a Union in C

我在 C 中看到此联合代码:

    union time
    {
      long simpleDate;
      double perciseDate;
    } mytime;

C 中的联合和结构有什么区别?你会在哪里使用工会,它有什么好处? Java、C++ 和/或 Python 中是否有类似的构造?

【问题讨论】:

标签: java c++ python c programming-languages


【解决方案1】:

联合的行为就好像它的所有成员都在内存中的同一位置,重叠。

当您想要表示某种“通用”值或可以是任何一组类型的值时,这很有用。由于字段重叠,因此只有在您知道该字段之前已初始化的情况下才能合法访问该字段。尽管如此,由于 C 不检查这一点,并且许多编译器发出允许它的代码,所以这是一个常见的技巧......有趣的类型转换,例如:

union {
 int integer;
 float real;
} convert;

convert.real = 3.14;
printf("The float %f interpreted as an integer is %08x", convert.real, convert.integer);

对于格式更正确的用法,您可以跟踪最后存储在联合中的内容,它可能看起来像这样:

typedef enum { INTEGER = 0, REAL, BOOLEAN, STRING } ValueType;

typedef struct {
  ValueType type;
  union {
  int integer;
  float real;
  char boolean;
  char *string;
  } x;
} Value;

这里,注意联合实际上是周围结构中的一个字段,Value。访问可能如下所示:

void value_set_integer(Value *value, int x)
{
  value->type = INTEGER;
  value->x.integer = x;
}

这记录了联合的当前内容是一个整数,并存储给定的值。一个函数,例如打印Value 可以检查type 成员,并做正确的事:

void value_print(const Value *value)
{
  switch(value->type)
  {
  case INTEGER:
    printf("%d\n", value->x.integer);
    break;
  case REAL:
    printf("%g\n", value->x.real);
    break;
  /* ... and so on ... */
  }
}

Java 中没有等价物。 C++ 几乎是 C 的超集,具有相同的功能。它甚至“one-ups” C 的实现,并允许anonymous unions。在 C++ 中,上面的代码可以不用命名内部联合 (x) 来编写,这会使代码更短。

【讨论】:

  • 重叠有问题吗?
  • 重叠的问题是你不知道最后写了什么。除了少数孤立的情况外,它们在所有情况下使用都很危险,但它们非常方便。
  • +1 表示“重叠”这一措辞,这可能是表达联合是什么的最优雅的方式。
【解决方案2】:

所以在你的例子中,当我分配时间时:

int main()
{
    time t;
}

编译器可以将 &t 处的内存解释为 long:

t.simpleDate;

或者好像它是双重的:

t.perciseDate;

所以如果 t 处内存的原始十六进制看起来像

0x12345678;

该值可以“解析”为双精度或长整数,具体取决于其访问方式。因此,要使其有用,您必须知道 long 和 double 将如何在内存中精确打包和格式化。例如,long 将是一个 2-s 补码有符号整数,您可以阅读有关 here 的信息。您可以了解如何以二进制格式 here 格式化双精度数。

然而,结构只是将具有不同地址间距的单独变量分组到一个内存块中。

(请注意,您的示例可能很危险,因为 sizeof(long) 可能是 32 位,而 sizeof(double) 始终是 64 位)

当您需要“原始”表示(如 char 数组)和“消息”表示时,通常使用联合。例如要通过套接字发送的消息:

struct Msg
{
   int msgType;
   double Val1;
   double Val2;
}; // assuming packing on 32-bit boundary

union
{
   Msg msg;
   unsigned char msgAsBinary[20];
};

希望对您有所帮助。

【讨论】:

  • 这是一个比当前发布的另一个更好的答案。
  • “为了有用,你必须知道 long 和 double 将如何被打包和格式化”。标准不保证但大多数编译器都使用联合的一种用法是编写一个成员,然后读回另一个成员以获得一种重新解释转换。对于此用途,您需要了解存储表示。然而,还有另一个(最初打算的)用途,即您存储任何成员,记住您存储的内容,然后读回同一个成员。目的是在您永远不需要同时使用两者时节省内存。为此,格式无关紧要。
  • @Steve Jessop:还有一条评论 (+1),其中包含关键点,应该合并到答案中。
【解决方案3】:

联合允许您以几种不同的方式解释一个内存位置(原始、二进制值)。

我实际使用的一个示例是访问 uint32 的各个字节。

union {
  uint32 int;
  char bytes[4];
} uint_bytes;

联合提供的是访问(部分)同一内存的多种方式。

联合类型的大小等于联合中最大类型的大小。

【讨论】:

    【解决方案4】:

    联合是一种存储多种不同类型“之一”的节省空间的方式。它不提供重新发现存储在其中的类型的机制;这必须离线确定。从技术上讲,访问联合中的“错误”类型(即未初始化的类型)会导致未定义的行为;在实践中,它通常会导致位级强制转换,并且经常被用作这样做的一种方式。

    虽然“联合”类型在 C++ 中(C++ 是 C 的超集),但大多数 C++ 类型不能安全地存储在一个中(具体来说,联合只能保存POD types,即具有默认复制构造函数的类,默认析构函数,没有虚方法)。如果你想要一个节省空间、基于堆栈的等价于 C++ 中的联合,并且能够存储复杂对象,请尝试Boost.Variant

    在其他不太关心堆栈分配的语言中,多态性完成了联合的工作。在 Java 中,一切都继承自 Object,因此 Object* 可以用来表示 any 对象;或者您可以使用通用超类或接口将对象集限制为支持特定操作集的对象。

    在 Python 中,任何变量都可以包含任何对象,因此在某种意义上,所有变量都是联合。您通常不需要确定存储在变量中的类型;相反,使用duck typing——也就是说,寻找它支持的方法而不是它实现的类型/接口。

    【讨论】:

    • 有关如何在 Java 和 Python(结构模块)中进行位级转换的示例将使其成为最完整的答案。
    • 由于严格来说不应将联合用于位级强制转换,我想说我将不再回答这个问题;)
    【解决方案5】:

    对于结构,每个数据项都有自己的内存位置,但对于联合,一次只使用一项,并且为每个项分配的内存在共享内存中。一个联合的数据项只会共享一个内存位置。联合的大小将是最大变量的大小。

    这可能是有益的,因为有时我们可能不需要复杂数据结构的所有(相关)数据项的数据,并且一次只存储/访问一个数据项。 Union 在这种情况下会有所帮助。

    【讨论】:

      【解决方案6】:

      与所有成员一起驻留在内存中的结构不同,一次只能使用联合的一个成员。因为联合空间的分配大小与其包含的最长元素相同。

      【讨论】:

        【解决方案7】:

        联合可用于存储它的任何一个成员,但(与结构不同)同时不能超过一个。您可以将其视为包含足够的空间来存储其最大的成员,并为您实际分配值的任何成员重新使用相同的存储空间。

        C++ 也有联合。 Java没有。 Python 中的对象成员的工作方式与 C 完全不同,它们存储在字典中,而不是在内存中连续布局。我不知道 Python 在某个地方是否有一些方便的库类,它的作用有点像联合,但它不像 C 语言那样对对象来说是基本的。

        【讨论】:

          猜你喜欢
          • 2020-08-25
          • 1970-01-01
          • 1970-01-01
          • 2015-07-31
          • 2014-08-04
          • 2020-03-12
          • 2012-12-08
          • 2015-01-30
          • 1970-01-01
          相关资源
          最近更新 更多