目录
C语言为具有可能值较少的变量提供了一种专用类型。枚举类型 是一种值由程序员列出(枚举)的类型,而且程序员必须为每个值命名。我们把这些名字称为 枚举常量。
/* 扑克牌花色成为整数的别名 */
enum {CLUBS, DIAMONDS, HEARTS, SPADES} s1, s2;
如果枚举声明在函数体内,那么它的常量对外部函数来说是不可见的。
16.5.1 枚举标记和类型名
命名枚举的两种方法:
- 通过声明标记的方法
- 使用 typedef 来创建独一无二的类型名
枚举标记类似于结构和联合的标记。
enum [枚举名] {枚举元素列表};
/* 声明了一个枚举类型enum Suit */
enum Suit {CLUBS, DIAMONDS, HEARTS, SPADES};
枚举变量 s1 和 s2 可以按照下列方法来声明:
enum Suit s1, s2;
枚举变量和其它数值型量不同,它们的取值只限于花括号中指定的值之一。因此,s1 和 s2 的值只能是 CLUBS, DIAMONDS, HEARTS, SPADES 之一。
也可以不声明有名字的枚举类型,而直接定义枚举变量,例如:
enum {CLUBS, DIAMONDS, HEARTS, SPADES} s1, s2;
还可以用 typedef 把 Suit 定义为类型名:
typedef enum {CLUBS, DIAMONDS, HEARTS, SPADES} Suit;
在 C89 中,利用 typedef 来命名枚举是创建布尔类型的一种非常好的方法:
typedef enum {FALSE, TRUE} Bool;
16.5.2 枚举作为整数
在系统内部,C语言会把枚举变量和枚举常量作为整数来处理。默认情况下,编译器会把整数 0, 1, 2, ... 赋给特定枚举中的常量。例如,在枚举 suit 的例子中,CLUBS、DIAMONDS、HEARTS 和 SPADES 分别表示 0、1、2 和 3。
我们可以为枚举常量自由选择不同的值:
enum suit {CLUBS = 1, DIAMONDS = 2, HEARTS = 3, SPADES = 4};
枚举常量的值可以是任意整数,也可以不用按照特定的顺序列出:
enum suit {CLUBS = 20, DIAMONDS = 10, HEARTS = 15, SPADES = 25};
当没有为枚举常量指定值时,它的值比前一个常量的值大 1 。并且在默认情况下,第一个枚举常量的值默认为 0 。
/* CLUBS = 0, DIAMONDS = 10, HEARTS = 11, SPADES = 25 */
enum suit {CLUBS, DIAMONDS = 10, HEARTS, SPADES = 25};
枚举的值只不过是一些稀疏分布的整数,所以C语言允许把它们与普通整数进行混合:
int i;
enum {CLUBS, DIAMONDS, HEARTS, SPADES} s;
i = DIAMONDS; /* i is now 1 */
s = 0; /* s is now 0 (CLUBS) */
s++; /* s is now 1 (DIAMONDS) */
i = s + 2; /* i is now 3 */
注意:虽然把枚举的值作为整数使用非常方便,但是把整数用作枚举的值是非常危险的。例如,我们可能会不小心把 4 存储到 s 中,而 4 不能跟任何花色相对应。
16.5.3 用枚举声明标记字段
所遇问题:
定义一种联合类型,它所包含的成员分别表示要存储在数组中的不同数据类型:
typedef union {
int i;
double d;
} Number; /* i 和 d 是联合 Number 的成员 */
接下来,创建一个数组,使数组的元素是 Number 类型的值:
Number number_array[1000];
可以在数组 number_array 中存储 int 和 double 的混合值:
number_array[0].i = 5;
number_array[1].d = 8.395;
假设编写了一个函数,用来显示当前存储在联合 Number 中的值。这个函数可能有下列框架:
void print_number(Number n) {
if (n 包含一个整数)
printf("%d", n.i);
else
printf("%g", n.d);
}
为了帮助函数 print_number 确定 n 包含的是整数还是浮点数,我们可以把联合嵌入到一个结构体中,并且此结构体还含有另一个成员:标记字段,它是用来提示当前存储在联合中的内容的。
下面把 Number 类型转换成具有嵌入联合的结构体类型:
#define INT_KIND 0
#define DOUBLE_KIND 1
typedef struct {
int kind; /* tag field */
union {
int i;
double d;
} u;
} Number;
每次给 u 的成员赋值时改变 kind 的值,从而提示修改的是 u 的哪个成员:
Number n;
n.kind = INT_KIND;
n.u.i = 82;
问题得到初步解决:
void print_number(Number n) {
if (n.kind == INT_KIND)
printf("%d", n.i);
else
printf("%g", n.d);
}
用枚举来解决上述问题是非常合适的:
typedef struct {
enum {INT_KIND, DOUBLE_KIND} kind; /* tag field */
union {
int i;
double d;
} u;
} Number;
不仅避免了使用宏定义——此时 INT_KIND 和 DOUBLE_KIND 是枚举常量——还阐明了 kind 的含义。现在 kind 显然只有两种可能的值:INT_KIND 和 DOUBLE_KIND。