我知道联合和结构之间的区别。 但从设计和编码的角度来看,使用联合而不是结构的各种用例是什么?一是空间优化。使用它们还有其他好处吗?
实际上只有两个主要用途。首先是创建一个歧视性工会。这可能就是您所想到的“空间优化”,但还有更多内容。您需要额外的数据位来了解联合的哪个成员是“活动的”(其中包含有效数据),因为编译器不会为您执行此操作。您通常会看到类似的代码在结构内部有一个联合,例如:
struct mixed {
enum { TYPE_INT, TYPE_FLOAT } type;
union {
int int_value;
float float_value;
} data;
};
当分配给 data.int_value 或 data.float_value 时,您还需要将类型成员设置为适当的枚举值。然后使用混合值的代码可以确定是读取 int_value 还是 float_value。
联合的第二个重要用途是提供一个 API,允许用户以多种方式访问相同的数据。这是相当有限的,因为它要求联合中的所有类型以相同的方式放置在内存中。例如,int 和 float 完全不同,将 float 作为整数访问不会给您任何特别有意义的数据。
有关此用例的有用示例,请查看有多少网络 API 将为 IPv4 地址定义联合,例如:
union ipv4addr {
unsigned address;
char octets[4];
};
大多数代码只想传递 32 位整数值,但有些代码想要读取各个八位位组(字节)。这一切都可以通过指针强制转换来实现,但它更容易一些,更自我记录,因此以这种方式使用联合稍微安全一些。
但总的来说,如果您不确定是否需要工会,那么您几乎肯定不需要工会。
当您的“事物”可以是许多不同事物之一但一次只能是一个时,您可以使用联合。
当你的“事物”应该是一组其他事物,并且所有这些事物可以同时存在时,你就使用结构。例如,联合可用于表示小部件
或小玩意(带有一个字段让我们知道它是什么),可以看起来像:
struct { // +-----------+
int is_widget; // | is_widget | |
union { // +-----------+ | increasing
widget w; // | w | g | | memory
gizmo g; // +-----+ | | addresses
} // | | V
} // +-----+
在这个例子中,w
和
g
会在内存中重叠,你可以在评论的右侧看到(这个例子中的小玩意更大)。我已经在编译器中看到过这种用法,其中标记可以是数字常量、关键字、变量名、字符串和许多其他词汇元素(但是,当然,每个标记只是其中的
一个,你不能拥有一个既是变量名又是数字常量的标记)。
或者,在没有小部件的情况下处理小玩意可能是非法的,在这种情况下,您可以使用:
struct { // +-----+
widget w; // | w | |
gizmo g; // +-----+ | increasing
} // | g | | memory
// | | | addresses
// | | V
// +-----+
在这种情况下,g
将位于不同的内存位置,位于
w
之后的某个位置(无重叠)。这种情况的用例比比皆是,例如包含电话簿应用程序记录布局的结构,这无疑将为您从您首选的应用程序商店赚取数百万美元:-)
实现可以将它们连续地放入内存中,因为编译器只需要表现得“好像”它们重叠,例如,如果您在设置 w
g
,则会发出抱怨。
但是,编译器这样做是不寻常的,因为它忽略了内存效率。
在某些情况下,我们可能一次只能使用一个变量,但不同的结果必须使用不同的名称存储。在这种情况下,联合将通过在具有最大变量大小的一个位置为每个变量分配相同的空间来提供帮助。
你不能将联合与结构进行比较,就像将苹果与橙子进行比较一样,它们用于不同的事物。联合通常用于空间宝贵的情况,但更重要的是用于专门的备用数据。联合有助于消除拼写错误并确保互斥状态保持互斥,因为当我们对互斥数据使用联合时,编程逻辑中的错误会更快地显现出来。