LV050-枚举类型
一、枚举类型简介
1. 什么是枚举?
在实际问题中,有些数据的取值往往是有限的,只能是非常少量的整数,并且最好为每个值都取一个名字,以方便在后续代码中使用,比如一个星期只有七天,一年只有十二个月,等。
针对上边那些特殊变量, C 语言为我们提供了枚举类型,在枚举的定义中,会将这些变量的值一一列举出来,并且枚举类型的变量的值就只限于列举出来的值的范围。
2. 怎么定义?
枚举类型的定义需要用到关键字 enum ,一般定义格式如下:
enum typeName
{
valueName1,
valueName2,
valueName3,
...
valueNameN,
};typeName 为枚举类型的名称, valueNameN 为枚举类型的成员。
(1)枚举类型中任意两个 枚举成员不能具有相同的名称。而且枚举列表中的 valueName1... 这些标识符的 作用范围是全局的(严格来说是 main() 函数内部),不能再定义与它们名字相同的变量。
(2)枚举类型中间的枚举成员之间用逗号( , )隔开,定义结束时,分号( ; )必不可少。
(3)在枚举类型中,枚举成员的类型只能是整型,并且声明的第一个枚举成员默认值是 0 ,往后逐个加 1 (递增)。这样增加后的值必须是整型可表示的值得范围,否则会报错。
(4)我们也可以在定义枚举类型时,为枚举成员显示赋值,允许多个枚举成员具有相同的值。没有显示赋值的枚举成员的值总是前一个枚举成员的值加 1 。例如,
enum Weeks
{
Monday = 1,
Tuesday = 2,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};(5)枚举与宏其实有些类似,宏在预处理阶段将名字替换成对应的值,枚举在编译阶段将名字替换成对应的值。我们可以将枚举理解为编译阶段的宏。
当我们的枚举类型定义完成后,枚举成员都是常量,它们不占用数据区(常量区、全局数据区、栈区和堆区)的内存,而是 直接被编译到命令里面,放到代码区,所以不能用 & 取得它们的地址。我们也无法修改枚举成员的值,除非直接修改相应枚举类型的定义。
3. 怎么使用?
枚举类型定义后就可以直接使用了,例如:
#include <stdio.h>
/* 定义枚举数据类型 */
enum __CMDS {
HOST_CMD_BASIC = 50,
HOST_CMD_1,
HOST_CMD_2,
USER_CMD_BASIC = 100,
USER_CMD_1,
USER_CMD_2,
INVALID_CMD = 200,
}; /* ; 不可缺少 */
int main(int argc, char *argv[])
{
printf("HOST_CMD_BASIC=%d, HOST_CMD_1=%d, HOST_CMD_2=%d\n", HOST_CMD_BASIC, HOST_CMD_1, HOST_CMD_2);
printf("USER_CMD_BASIC=%d, USER_CMD_1=%d, USER_CMD_2=%d\n", USER_CMD_BASIC, USER_CMD_1, USER_CMD_2);
printf("INVALID_CMD=%d\n", INVALID_CMD);
return 0;
}将会得到下面的输出:
sumu@virtual-machine:~/hk/alpha$ gcc main.c -Wall
sumu@virtual-machine:~/hk/alpha$ ./a.out
HOST_CMD_BASIC=50, HOST_CMD_1=51, HOST_CMD_2=52
USER_CMD_BASIC=100, USER_CMD_1=101, USER_CMD_2=102
INVALID_CMD=2004. 枚举常量作用域
枚举类型中的枚举常量也是一个有效的标识符,可以拿出来单独使用,这一点和普通常量非常类似。所以说,枚举类型仅仅是将不同名字的常量松散地组织到了一起。
枚举常量的作用域和其它自定义标识符(比如变量)的作用域类似:
- 在所有函数外部定义枚举类型时,其中的枚举常量具有全局作用域,可以在整个程序中的任何位置访问。这和全局变量是一样的。
- 在由
{ }包围的代码块(比如函数、if、for、while 范围就无效了。这个局部变量是一样的。
#include <stdio.h>
// 全局枚举定义
enum Colors {RED, GREEN, BLUE};
int main() {
printf("RED = %d\n", RED); // 可以访问全局枚举常量
enum Direction {NORTH, SOUTH, EAST, WEST};
printf("NORTH = %d\n", NORTH); // 可以访问局部枚举常量
{
enum Status {OK = 200, ERROR = 500};
printf("OK = %d\n", OK); // 可以访问块作用域内的枚举常量
}
// printf("OK = %d\n", OK); // 错误:OK 超出了作用域范围
return 0;
}将会得到下面的输出:
RED = 0
NORTH = 0
OK = 2005. 为什么要用枚举
- 增强了代码的可读性,通过使用有意义的名称代替没有名称的数字,使代码更易理解。
- 提高了类型安全性,编译器可以检查枚举变量是否被赋予了有效的值。
但枚举类型也有一些限制。例如,C 语言不允许为枚举常量指定浮点数值,也不能在运行时动态地向枚举类型添加新的常量。此外,不同的编译器可能会对枚举类型使用不同的底层整数类型,这可能会影响程序的可移植性。
这里举一个例子,比如枚举类型非常适合用在 switch 语句中,因为它们代表了一组离散的值,这样可以提高代码的可读性和可维护性,通过使用有意义的名称替代乱七八糟的数字,可以使代码更加清晰和易于理解:
enum Week day = Wednesday;
switch(day) {
case Monday:
printf("It's Monday, start of the work week.\n");
break;
case Wednesday:
printf("It's Wednesday, halfway through!\n");
break;
case Friday:
printf("It's Friday, weekend is coming!\n");
break;
default:
printf("It's another day of the week.\n");
}二、枚举变量
1. 怎么定义?
与结构体一样,枚举也是一种构造的数据类型,所以可以像结构体一样定义枚举变量。枚举变量定义一般格式如下:
1.1 方式一
- 先定义枚举数据类型,再声明变量
/* 定义枚举数据类型 */
enum Weeks
{
Monday = 1,
Tuesday = 2,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
};
/* 定义枚举类型变量 */
enum Weeks w1, w2;定义了两个枚举变量 w1 和 w2 。需要注意关键字 Weeks 不可以省略,若没有这个关键字,则系统不认为 Weeks 是枚举类型。
1.2 方式二
- 定义枚举类型的同时声明枚举变量
/* 定义枚举类型的同时声明枚举变量 */
enum Weeks
{
Monday = "1",
Tuesday = "2",
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}w1, w2;与方式一类似,定义了两个枚举变量 w1 和 w2 。如果只需要 w1 、 w2 两个变量,后面不需要再使用枚举类型名称定义其他变量,那么在定义时也可以不给出枚举类型名称。
/* 定义枚举类型的同时声明枚举变量 */
enum
{
Monday = "1",
Tuesday = "2",
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}w1, w2;2. 枚举变量的使用
2.1 引用枚举成员
枚举类型名1
枚举类型名2
...
枚举类型名N2.2 枚举变量赋值
定义枚举类型以后,我们可以声明枚举类型的变量,并为其赋值了。枚举变量可以被赋予在枚举类型中定义的任何常量值,其语法格式如下:
enum 枚举类型名 枚举变量名 = 枚举常量名;枚举类型变量赋值方式和结构体类似,接下来通过实例说明。需要注意的是,虽然枚举类型的本质是整型,但直接将整数赋值给枚举变量是不安全的做法,因为这可能导致变量取值超出枚举定义的范围。如果确实需要这样做,应该使用强制类型转换:
enum Week today = (enum Week)3; // 等价于 Wednesday2.2.1 赋值方式一
- 先定义枚举类型变量,再赋值
#include <stdio.h>
/* 定义枚举数据类型 */
enum Weeks
{
Monday = 1,
Tuesday = 2,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}; /* ; 不可缺少 */
int main(int argc, char *argv[])
{
enum Weeks w1, w2, w3;
w1 = Monday;
w2 = Sunday;
w3 = 9;
printf("%d %d %d %d %d %d %d\n", Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
printf("%d %d %d \n", w1, w2, w3);
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
1 2 3 4 5 6 7
1 7 9【说明】我们可以把定义枚举类类型时的枚举成员赋值给枚举变量,也可以直接把整数值赋给枚举变量。枚举变量其实也就是一种 整型变量。
2.2.2 赋值方式二
- 定义枚举变量的同时进行赋值
#include <stdio.h>
/* 定义枚举数据类型 */
enum Weeks
{
Monday = 1,
Tuesday = 2,
Wednesday,
Thursday,
Friday,
Saturday,
Sunday
}; /* ; 不可缺少 */
int main(int argc, char *argv[])
{
enum Weeks w1 = Monday, w2 = Friday, w3 = 10;
printf("%d %d %d %d %d %d %d\n", Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday);
printf("%d %d %d \n", w1, w2, w3);
return 0;
}在终端执行以下命令:
gcc test.c -Wall # 编译程序
./a.out # 执行可执行文件会看到有如下信息输出:
1 2 3 4 5 6 7
1 5 102.2.3 赋值方式三
- 在定义枚举类型的同时声明变量:
enum Color {Red, Green, Blue} my_color1;
enum Color {Red, Green, Blue} my_color2 = Green;2.2.4 互相赋值
枚举变量之间也可以相互赋值,但前提是这两个变量必须属于同一个枚举类型,例如:
enum Color my_color1;
enum Color my_color2 = Green;
my_color1 = my_color2;三、枚举类型与整数类型
枚举类型可以与整数类型进行比较和赋值。由于枚举常量本质上是整数,我们可以将枚举变量与整数进行比较,也可以将整数赋值给枚举变量(尽管这种做法可能会导致类型安全问题),或者反过来,将枚举变量赋值给整数类型:
enum Week day = Tuesday;
if (day < Thursday) {
printf("It's early in the week.\n");
}
int num = Friday; // num 的值为 4day = 6; // day 的值为 Saturday