结构体与枚举类型 - struct & enum
之前我们讲过基本数据类型,但是实际上除了基本数据类型以外,C 语言还有其他的数据类型,如下图:

像 int、float、char 等是由 C 语言本身提供的数据类型,不能再进行分拆,我们称之为 基本数据类型
;而数组、结构体可以包含多个基本类型的数据,我们将它称为 复杂数据类型
或 构造数据类型
。
前面的教程中我们讲解了数组(Array),它是一组具有相同类型的数据的集合。但在实际的编程过程中,我们往往还需要一组类型不同的数据,例如描述学⽣需要名字、年龄、学号、⾝⾼、体重、成绩等信息,名字为字符串,年龄为整数,成绩为浮点数,因为数据类型不同,显然不能用一个数组来存放。
在 C 语言中,可以使用结构体(Struct)来存放一组不同类型的数据。本节课我们就来学习结构体的相关内容。
结构体的定义与使用
在 C 语言中,可以使用 结构体
来将多种不同的数据类型组装起来,形成某种现实意义的自定义的变量类型。
结构体的语法如下:
struct 结构体名
{
成员1;
成员2;
...
};
其中的成员为结构体所包含的变量或数组,例如存储学生的名字、年龄、身高、性别,名字是字符串类型,年龄为整型,身高为浮点型,性别为字符,代码如下:
#include <stdio.h>
// 结构体应写在 main 函数外面
struct student_info {
char name[10]; // 名字
int age; // 年龄
double height; // 身高
char gender; // 性别
};
int main() {
return 0;
}
需要注意的是,结构体的代码应该写在 main 函数的外部,如果将结构体写在 main 函数中,那么,我们只能在 main 函数中使用该数据类型,与局部变量同理。
现在,除了 int、float、double、char 等 C 语言与生俱来的类型外,我们还有了 struct student_info
这种自定义数据类型,既然结构体是一种数据类型,那么就可以用它来定义变量。例如现在我们可以使用 struct student
这种类型来定义变量:
struct student_info student_1;
struct student_info student_2, student_3;
注意:struct 不可省略!因为自定义数据类型为 struct student_info
,而不是 student_info
,错误代码如下:
// 错误代码演示
student_info student_4;
定义完变量后,我们就需要对变量进行赋值操作。结构体和数组类似,也是一组数据的集合,赋值时使用大括号将结构体中成员对应的值按顺序填写进来即可,代码如下:
#include <stdio.h>
struct student_info {
char name[10]; // 名字
int age; // 年龄
double height; // 身高
char gender; // 性别
};
int main() {
// 整体赋值
struct student_info stu_1 = {"Alice", 18, 168.5, 'F'};
return 0;
}
结构体使用点号 .
获取单个成员。获取结构体成员的一般格式为:
结构体变量名.成员名;
因此,我们可以通过以上形式来访问结构体变量 stu_1
中的成员,代码如下:
printf("名字:%s, 年龄:%d, 身高:%.1f, 性别:%c\n", stu_1.name, stu_1.age, stu_1.height, stu_1.gender);
除了整体赋值外,也可以对结构体成员进行逐一赋值,代码如下:
#include <stdio.h>
#include <string.h>
struct student_info {
char name[10]; // 名字
int age; // 年龄
double height; // 身高
char gender; // 性别
};
int main() {
// 在使用过程中对成员逐一进行赋值操作
struct student_info stu_2;
stu_2.age = 20;
stu_2.height = 181;
stu_2.gender = 'M';
strcpy(stu_2.name, "Bob");
printf("名字:%s, 年龄:%d, 身高:%.1f, 性别:%c\n", stu_1.name, stu_1.age, stu_1.height, stu_1.gender);
return 0;
}
typedef
现在,我们在定义 struct student_info
类型的变量时,需要把 struct 关键字加上,这种写法比较冗长,尤其是在频繁使用结构体时,代码会显得不够简洁。
为了避免每次都写 struct 关键字,C 语言提供了 typedef,允许为类型创建一个更简洁的别名,语法结构如下:
typedef struct 结构体名 {
...
} 别名
通过 typedef,我们可以直接用 student
代替 struct student_info
,使代码更加简洁和易读,代码如下:
#include <stdio.h>
typedef struct student_info {
char name[10]; // 名字
int age; // 年龄
double height; // 身高
char gender; // 性别
}student;
int main() {
student stu_1 = {"Alice", 18, 1.68, 'F'};
student stu_2 = {"Bob", 19, 1.88, 'M'};
student stu_3 = {"Cathy", 17, 1.63, 'F'};
return 0;
}
typedef 除了可以提高可读性,让代码变得更简洁外,最重要的是他可以使代码在 C 和 C++ 中都兼容。
当然,使用 typedef 并不是强制的,完全取决于个人或团队的编码风格。如果你不介意在声明结构体变量时写 struct 关键字,也可以不使用 typedef。
结构体数组
以上代码中的 stu_1、stu_2 本质上仍然是变量,只不过变量的数据类型是自定义的,因此,我们也可以使用 struct student_info
来创建数组,代码如下:
#include <stdio.h>
typedef struct student_info {
char name[10]; // 名字
int age; // 年龄
double height; // 身高
char gender; // 性别
} student;
int main() {
student stu_1 = {"Alice", 18, 1.68, 'F'};
student stu_2 = {"Bob", 19, 1.88, 'M'};
student stu_3 = {"Cathy", 17, 1.63, 'F'};
student stu_4 = {"David", 18, 1.75, 'M'};
student stu_array[4] = {stu_1, stu_2, stu_3, stu_4};
for (int i = 0; i < 4; ++i) {
printf("姓名:%s, 年龄:%d, 身高:%.2f, 性别:%c\n", stu_array[i].name, stu_array[i].age, stu_array[i].height, stu_array[i].gender);
}
return 0;
}
enum - 枚举类型
我们在刚开始学习编程的时候,代码中经常会出现 魔术数字(magic number)
,在编程领域指的是莫名其妙出现的数字,数字的意义必须通过详细阅读才能推断出来。虽然代码原作者写的时候自己能了解数值的意义,但对其他程序员而言,甚至制作者本人经过一段时间后,会难以了解这个数值的用途,只能苦笑讽刺“这个数值的意义虽然不懂,不过至少程序会动,真是个魔术般的数字”而得名。
示例如下:
price_discount = 0.75 * price;
以上代码是对输入的价格(price)计算打折后价格(price_discount)的公式。在这里,0.75 表示七五折,但是 0.75 是一种魔术数字,无法让人一眼就明白这个数字是什么意思。一般魔法数字都是需要使用 枚举变量
来替换的。
enum(枚举类型)
是 C 语言中的一种数据类型,通常用于为程序中的一组相关的常量取名字。它使得程序中的常量更加具有可读性和可维护性。通过 enum 可以将一组相关的常量集中在一起,赋予它们特定的名字,简化代码并减少错误。
定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。
在 C 语言中,定义一个枚举类型的基本语法如下:
enum 枚举名 {
枚举元素1 = 整数1,
枚举元素2 = 整数2,
枚举元素3 = 整数3,
枚举元素4 = 整数4,
枚举元素5 = 整数5,
……
};
例如,一星期有 7 天,我们可以使用枚举的方式来定义:
enum weekdays {
MON = 1,
TUE = 2,
WED = 3,
THU = 4,
FRI = 5,
SAT = 6,
SUN = 7
};
通过以上代码,我们就创建了 7 个标识符并指定了值。如果没有对 MON ... SUN 这些标识符进行赋值,代码如下:
enum weekdays {
MON,
TUE,
WED,
THU,
FRI,
SAT,
SUN
};
C 编译器会默认从 0 开始递增,MON = 0,TUE = 1,...,SUN = 6。定义枚举的意义就是给这些常量值名字。
验证代码如下:
#include <stdio.h>
//enum weekday
//{
// MON = 1, TUE = 2, WED = 3, THU = 4, FRI = 5, SAT = 6, SUN = 7
//};
//enum weekday
//{
// MON, TUE, WED, THU, FRI, SAT, SUN
//};
enum weekday
{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
};
int main() {
enum weekday today;
today = FRI;
printf("今天是星期 %d\n", today);
return 0;
}
我们也可以只对第 1 个标识符进行赋值操作,这样,后面的标识符的值就会依次加 1。
使用 enum 与 switch 语句来判断日期,代码就会显得更加简洁易懂,代码如下:
#include <stdio.h>
enum weekday
{
MON = 1, TUE, WED, THU, FRI, SAT, SUN
};
int main() {
enum weekday today;
printf("今天是周几?");
scanf("%d", &today);
switch (today) {
case MON:
printf("今天是周一\n");
break;
case TUE:
printf("今天是周二\n");
break;
case WED:
printf("今天是周三\n");
break;
case THU:
printf("今天是周四\n");
break;
case FRI:
printf("今天是周五\n");
break;
case SAT:
printf("今天是周六\n");
break;
case SUN:
printf("今天是周日\n");
break;
default:
printf("日期无效\n");
}
return 0;
}
使用 enum 可以将数字常量替换为具有语义意义的名称,这样代码变得更加直观和易于维护。例如:
enum status {
SUCCESS = 0,
ERROR = -1
};
int function() {
return ERROR; // 更具语义意义
}