枚举类型
枚举是一个常量整型值的列表。通过使用关键字enum,可以创建一个新类型,并指定它可以具有的值。在定义时如果不指定枚举类型名称,则会创建一个匿名枚举,其语法如下:
enum 标识符(可选) { 枚举常量1, 枚举常量2, ... 枚举常量n };
例如:
// 定义枚举类型Week,它含有7个常量项:SUN、MON、TUE...
enum Week { SUN, MON, TUE, WED, THU, FRI, SAT };
// 定义一个匿名枚举,它拥有2个常量项:NO、YES。
enum { NO, YES };
在没有显式说明的情况下,enum类型中第一个枚举常量的值为0,第二个为1,依次类推。
#include <stdio.h>
enum Week { SUN, MON, TUE, WED, THU, FRI, SAT };
int main()
{
printf("SUN: %d\n", SUN);
printf("MON: %d\n", MON);
printf("SAT: %d\n", SAT);
return 0;
}

同一枚举中不同的枚举常量可以具有相同的值;枚举常量属于int类型,如果只指定部分枚举名的值,那么未指定值的枚举名的值将依着最后一个指定值向后递增。注意E8的值0xFFFFFFFF,它被解释为int,是个有符号数,对它进行补码运行,得到的基数为1,即0xFFFFFFFF的值就是-1。
#include <stdio.h>
int main()
{
enum Values
{
E0,
E1 = 2,
E2,
E3,
E4 = -1,
E5 = -2,
E6,
E7,
E8 = 0xffffffff,
E9
};
printf("E0: %d\n", E0);
printf("\n");
printf("E1: %d\n", E1);
printf("E2: %d\n", E2);
printf("E3: %d\n", E3);
printf("\n");
printf("E4: %d\n", E4);
printf("E5: %d\n", E5);
printf("E6: %d\n", E6);
printf("E7: %d\n", E7);
printf("\n");
printf("E8: %X\n", E8);
printf("E9: %X\n", E9);
return 0;
}

引入枚举类型可以提高程序的可读性,重要的是它引入了类型的概念!同内置类型一样,我们可以定义枚举类型的变量、指针等。
enum Color { RED, GREEN, BLUE };
enum Color c = RED;
enum *cp = &c;
枚举常量通常是int类型,但枚举变量较宽松地限定为任一种整数类型,只要该整数类型能保存这些枚举常量。通常情况下,枚举变量与char、有符号整数类型或无符号整数类型兼容。
enum { ONE = 1, TWO };
enum e;
long n = ONE; // 提升:枚举常量ONE从int到long
double d= ONE; // 转换:从int到double
e = 1.2; // 转换:从double到enum, e值为ONE
e = e + 1; // 赋值:e值现在是TWO
当然,由于匿名枚举没有名称,自然无法定义其变量了。
#define 还是 enum
设想一下我们开发的一款2D角色扮演游戏,其中需要定义玩家的行动方位,这可以通过#define实现,如:
#define LEFT 0
#define RIGH 1
#define UP 2
#define DOWN 3
我们知道,#define仅仅是预编译时的文本替换,在经过预编译之后,LEFT/RIGHT这些名称都已经消失,仅剩下0、1等这些字面常量。正是如此,当我们需要一个操纵角色移动的函数时,通常会选用整型类型指示这些方向参数,一种实现可能如下:
void move(int dir)
{
if (dir == LEFT) ;
else if (dir == RIGHT) ;
}
我们在这里选择int作为这些方位的基类型,深层次的原因是,我们无法表达上述“方位”这个类型。但如果使用枚举,我们便拥有了方位类型的概念,这可以使我们接口更加明确一些。
enum Direction
{
LEFT,
RIGH,
UP,
DOWN
};
void move(enum Direction dir)
{
if (dir == LEFT) ;
else if (dir == RIGHT) ;
}
如果有一种类型能够明确的表示当前函数需要的参数为一个“移动方向”类型,那函数的接口将更加友好,使用枚举类型(enum)可以做到这一点。
枚举元素数量
在没有显式指定枚举值的情况下,使用最后一个枚举项作为枚举数量计数器,是种非常有用的设计技巧。
enum Colors
{
RED,
GREEN,
BLUE,
WHITE,
BLANK,
COLOR_COUNT
};
这里,COLOR_COUNT枚举量被用作计量值,这在循环与数组定义中相当有用,如:
enum colors[COLOR_COUNT];
for (int i = 0; i < COLOR_COUNT; i++)
{
// balabala...
}
所属域
枚举可以定义在全局域(如示例一中的Week枚举);也可以定义在函数域(如示例二中的Values枚举)。通常,我们希望定义的枚举量能够在多个不同的编译单元中引用,这暗示在全局域定义枚举的机会更多。与函数、分支语句不同的是,枚举定义的左右大括号,只是语法形式上的要求,并不会形成一个作用域。也就是说在某一个作用域中定义的一个枚举,它的所有枚举值便位于此作用域。假如该作用域存在另一个枚举类型,其枚举值与之前某个枚举的枚举值相同,就会发生重复定义问题。
如,在全局域有一个Direction枚举,
enum Direction { FORWARD, BACK, LEFT, RIGHT };
此后,我们为按键控制在全局域定义了方向键枚举:
enum Key { LEFT, RIGHT, UP, DOWN };
这里 Direction 与 Key 虽然是两个不同的枚举类型,但由于枚举值会暴露于当前作用域(这里是全局作用域),出现了两个LEFT、RIGHT名称,发生了重定义问题。
enum Direction { FORWARD, BACK, LEFT, RIGHT };
enum Key { LEFT, RIGHT, UP, DOWN };
int main()
{
return 0;
}

基于这样的原因,建议在定义枚举值时附加上当前枚举的类型修饰。如:
enum Key { KEY_LEFT, KEY_RIGHT, KEY_UP, KEY_DOWN };
陕公网安备61011202001108号