联合(union)

联合(union)在许多其他语言中被称为变体记录(variant record)。它的外表与结构体相似,但在内存布局上存在关键性的区别。在结构体中,每个成员依次存储,而在联合中,所有的成员都从偏移地址零开始存储。这样,每个成员的位置都重叠在一起;在某一时刻,只有一个成员真正存储于该地址。

联合既有一些优点,也有一些缺点。它的缺点就是那些所谓的优点其实并不怎么出色。联合的优点是它的外观同结构体一样,只是用关键字union取代了关键字struct。所以,如果你对结构体的一切都已了如指掌,基本上也就掌握了联合。联合的一般形式如下:

1
2
3
4
5
6
union 联合标签(可选){
类型1 标识符1;
类型2 标识符2;
...
类型N 标识符N;
}变量定义(可选);

联合一般是作为大型结构的一部分存在的。在有些大型结构中,存在一些与实际表示的数据类型有关的隐式或显式的信息。如果存储数据时是一种类型,但在提取该数据时却成了另外一种类型,这显然存在着明显的类型不安全性。在Ada语言中,所有不同类型的字段都显式的存储于记录中,这就避免了这个问题。C语言则含糊的多,让程序员自己去回忆放在那儿的究竟是什么东西。

联合一般用来节省空间,因为有些数据项是不可能同时出现的,如果同时存储它们,显然颇为浪费。例如,如果我们要存储一些关于动物种类的信息,首先想到的方法可能是:

1
2
3
4
5
struct creature{
char has_backbone;
char has_fur;
short num_of_legs_in_excess_of_4;
};

但是,我们知道,所有的动物要么是脊椎动物,要么是无脊椎动物。进而,我们还知道只有脊椎动物才可能有毛皮,只有无脊椎动物才可能有多于4条的腿。没有一种动物既有毛皮又有超过4条的腿。这样,可以通过把两个互相排斥的字段存储于一个联合中来节省空间:

1
2
3
4
5
6
7
8
union secondary_characteristics{
char has_fur;
short num_of_legs_in_excess_of_4;
};
struct creature{
char has_backbone;
union secondary_characteristics form;
};

我们通常采取这种方式来节省备用的存储空间。如果我们有一个数据文件,里面存储了20000000个动物,使用这种方法,可以节省大约20MB的磁盘空间。

然而,联合还有其他用途,联合也可以把同一个数据解释成两种不同的东西,而不是把两个不同的数据解释为一个东西。该用法例子如下:

1
2
3
4
union bits32_tag{
int whole; //一个32位的值
struct { char c0, c1, c2, c3;} bytes; //4个8位的字节
} value;

这个联合允许程序员提取整个32位值(作为 int),也可以提取单独的字节字段如value.bytes.c0等。采用其他的方法也能达到这个目的,但联合不需要额外的赋值或强制类型转换。

在实际工作中,结构体的使用比联合多得多。

枚举(enum)

枚举(enum)通过一种简单的途径,把一串名字与一串整型值联系在一起。对于像C这样的弱类型语言而言,很少有什么事只能靠枚举而不能用#define来解决的。枚举的一般形式:

1
enum 可选标签 {内容...} 可选变量定义;

其中的“内容…”是把一些标识符的列表,可能有一些整型值赋给它们。下面是一个枚举实例:

1
enum sizes { small = 7, medium, large = 10, humungous };

缺省情况下,整型值从0开始。如果对列表中的某个标识符进行了赋值,那么紧接其后的那个标识符的值就比所赋的值大1,然后类推。枚举具有一个优点:#define 定义的名字一般在编译时被丢弃,而枚举名字则通常一直在调试器中可见,可以在调试代码时使用它们。