什么时候应该使用工会?我们为什么需要它们?


当前回答

工会是伟大的。我所见过的联合的一个聪明用法是在定义事件时使用它们。例如,您可能决定一个事件是32位的。

现在,在这32位中,您可能希望将前8位指定为事件发送方的标识符……有时你要把事件作为一个整体来处理,有时你要剖析它并比较它的组成部分。工会让你可以灵活地做到这两点。

union Event
{
  unsigned long eventCode;
  unsigned char eventParts[4];
};

其他回答

我在为嵌入式设备编码时使用union。我有一个16位的C整数。当我需要从/存储到EEPROM时,我需要检索高8位和低8位。所以我用了这种方法:

union data {
    int data;
    struct {
        unsigned char higher;
        unsigned char lower;
    } parts;
};

它不需要移动,所以代码更容易阅读。

另一方面,我看到一些旧的c++ stl代码使用联合的stl分配器。如果您感兴趣,可以阅读sgi stl源代码。下面是其中的一段:

union _Obj {
    union _Obj* _M_free_list_link;
    char _M_client_data[1];    /* The client sees this.        */
};

一个简单而有用的例子是....

想象一下:

你有一个uint32_t数组[2],想要访问字节链的第3个和第4个字节。 你可以做*((uint16_t*) &数组[1])。 但遗憾的是,这打破了严格的混叠规则!

但是已知的编译器允许你做以下事情:

union un
{
    uint16_t array16[4];
    uint32_t array32[2];
}

严格来说,这仍然是违反规则的。但是所有已知的标准都支持这种用法。

在C的早期版本中,所有结构声明都共享一组公共字段。考虑到:

struct x {int x_mode; int q; float x_f};
struct y {int y_mode; int q; int y_l};
struct z {int z_mode; char name[20];};

a compiler would essentially produce a table of structures' sizes (and possibly alignments), and a separate table of structures' members' names, types, and offsets. The compiler didn't keep track of which members belonged to which structures, and would allow two structures to have a member with the same name only if the type and offset matched (as with member q of struct x and struct y). If p was a pointer to any structure type, p->q would add the offset of "q" to pointer p and fetch an "int" from the resulting address.

Given the above semantics, it was possible to write a function that could perform some useful operations on multiple kinds of structure interchangeably, provided that all the fields used by the function lined up with useful fields within the structures in question. This was a useful feature, and changing C to validate members used for structure access against the types of the structures in question would have meant losing it in the absence of a means of having a structure that can contain multiple named fields at the same address. Adding "union" types to C helped fill that gap somewhat (though not, IMHO, as well as it should have been).

An essential part of unions' ability to fill that gap was the fact that a pointer to a union member could be converted into a pointer to any union containing that member, and a pointer to any union could be converted to a pointer to any member. While the C89 Standard didn't expressly say that casting a T* directly to a U* was equivalent to casting it to a pointer to any union type containing both T and U, and then casting that to U*, no defined behavior of the latter cast sequence would be affected by the union type used, and the Standard didn't specify any contrary semantics for a direct cast from T to U. Further, in cases where a function received a pointer of unknown origin, the behavior of writing an object via T*, converting the T* to a U*, and then reading the object via U* would be equivalent to writing a union via member of type T and reading as type U, which would be standard-defined in a few cases (e.g. when accessing Common Initial Sequence members) and Implementation-Defined (rather than Undefined) for the rest. While it was rare for programs to exploit the CIS guarantees with actual objects of union type, it was far more common to exploit the fact that pointers to objects of unknown origin had to behave like pointers to union members and have the behavioral guarantees associated therewith.

我在几个库中看到过它作为面向对象继承的替代品。

E.g.

        Connection
     /       |       \
  Network   USB     VirtualConnection

如果你想让Connection“类”是上面的任何一个,你可以这样写:

struct Connection
{
    int type;
    union
    {
        struct Network network;
        struct USB usb;
        struct Virtual virtual;
    }
};

libinfinity示例:http://git.0x539.de/?p=infinote.git;a=blob;f=libinfinity/common/inf-session.c;h=3e887f0d63bd754c6b5ec232948027cbbf4d61fc;hb=HEAD#l74

当你有一个函数,你返回的值可以不同,这取决于函数做了什么,使用联合。