什么时候应该使用工会?我们为什么需要它们?
当前回答
许多答案都涉及从一种类型转换到另一种类型。我从具有相同类型的联合中得到最多的使用(即在解析串行数据流时)。它们允许解析/构造一个有框架的包变得很简单。
typedef union
{
UINT8 buffer[PACKET_SIZE]; // Where the packet size is large enough for
// the entire set of fields (including the payload)
struct
{
UINT8 size;
UINT8 cmd;
UINT8 payload[PAYLOAD_SIZE];
UINT8 crc;
} fields;
}PACKET_T;
// This should be called every time a new byte of data is ready
// and point to the packet's buffer:
// packet_builder(packet.buffer, new_data);
void packet_builder(UINT8* buffer, UINT8 data)
{
static UINT8 received_bytes = 0;
// All range checking etc removed for brevity
buffer[received_bytes] = data;
received_bytes++;
// Using the struc only way adds lots of logic that relates "byte 0" to size
// "byte 1" to cmd, etc...
}
void packet_handler(PACKET_T* packet)
{
// Process the fields in a readable manner
if(packet->fields.size > TOO_BIG)
{
// handle error...
}
if(packet->fields.cmd == CMD_X)
{
// do stuff..
}
}
编辑 关于字节序和结构填充的评论是有效的,而且非常值得关注。我几乎完全在嵌入式软件中使用了这段代码,其中大部分我都可以控制管道的两端。
其他回答
在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.
工会是伟大的。我所见过的联合的一个聪明用法是在定义事件时使用它们。例如,您可能决定一个事件是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. */
};
有很多用法。只需执行grep union /usr/include/*或类似目录。大多数情况下,联合被包装在结构中,结构的一个成员告诉联合中的哪个元素可以访问。例如,为现实生活的实现签出man elf。
这是基本原则:
struct _mydata {
int which_one;
union _data {
int a;
float b;
char c;
} foo;
} bar;
switch (bar.which_one)
{
case INTEGER : /* access bar.foo.a;*/ break;
case FLOATING : /* access bar.foo.b;*/ break;
case CHARACTER: /* access bar.foo.c;*/ break;
}