对于联合,您只能使用其中一个元素,因为它们都存储在同一位置。当您想要存储可能是多种类型之一的内容时,这非常有用。另一方面,结构体的每个元素都有一个单独的内存位置,并且它们都可以立即使用。
为了给出它们使用的具体示例,不久前我正在开发一个Scheme解释器,并且我本质上是将Scheme数据类型覆盖到C数据类型上。这涉及在结构中存储一个指示值类型的枚举和一个用于存储该值的联合。
union foo {
int a; // can't use both a and b at once
char b;
} foo;
struct bar {
int a; // can use both a and b simultaneously
char b;
} bar;
union foo x;
x.a = 3; // OK
x.b = 'c'; // NO! this affects the value of x.a!
struct bar y;
y.a = 3; // OK
y.b = 'c'; // OK
edit:如果您想知道将 x.b 设置为“c”会将 x.a 的值更改为什么,从技术上讲,它是未定义的。在大多数现代机器上,char 为 1 个字节,int 为 4 个字节,因此为 x.b 提供值“c”也会为 x.a 的第一个字节提供相同的值:
union foo x;
x.a = 3;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);
prints
99, 99
为什么两个值相同?因为 int 3 的最后 3 个字节全为零,所以它也读为 99。如果我们为 x.a 放入一个更大的数字,您会发现情况并非总是如此:
union foo x;
x.a = 387439;
x.b = 'c';
printf("%i, %i\n", x.a, x.b);
prints
387427, 99
为了更仔细地查看实际内存值,让我们设置并打印出十六进制值:
union foo x;
x.a = 0xDEADBEEF;
x.b = 0x22;
printf("%x, %x\n", x.a, x.b);
prints
deadbe22, 22
您可以清楚地看到 0x22 覆盖了 0xEF 的位置。
BUT
在 C 中,int 中的字节顺序是没有定义的。该程序在我的 Mac 上用 0x22 覆盖了 0xEF,但在其他平台上它会覆盖 0xDE,因为组成 int 的字节顺序被颠倒了。因此,在编写程序时,永远不应该依赖于覆盖联合中特定数据的行为,因为它不可移植。
有关字节顺序的更多阅读,请查看字节顺序 https://en.wikipedia.org/wiki/endianness.