:结构体、共用体与枚举)
一、结构体1.1 为什么需要结构体假设要实现一个学生管理系统需要存储每个学生的姓名、年龄、成绩。如果用数组来管理char name[30][20]; // 30个学生的姓名 int age[30]; // 30个学生的年龄 int score[30]; // 30个学生的成绩数据被分散在多个数组中既不直观也不方便管理。结构体可以把相关联的不同类型的数据组合在一起形成一个新的数据类型。1.2 结构体类型的定义struct 结构体名 { 成员1; 成员2; ... };示例定义一个学生类型struct student { char name[20]; int age; float score; };各部分说明部分说明struct关键字用来说明是结构体类型student结构体名是新构造出来的类型的名称属于标识符符合标识符命名规则{}内部成员列表每个成员可以是不同类型此时struct student就是一个完整的类型名可以像基本类型一样使用。1.3 结构体变量的定义定义好结构体类型之后就可以像基本类型一样使用定义变量、定义数组、定义指针、做函数返回值类型等。三种定义方式方式一先定义类型再定义变量推荐struct stu { char name[20]; int age; float score; }; // 先定义结构体类型 struct stu s; // 再定义变量方式二定义类型的同时定义变量struct stu { char name[20]; int age; float score; } s {tom, 18, 99.5};如果结构体定义在全局则相当于定义了一个全局变量。方式三直接定义结构体类型变量匿名结构体struct { char name[20]; int age; float score; } s {tom, 18, 99.5};这种写法一般在结构体类型只用一次的时候使用。1.4 结构体的初始化按照成员顺序逐个初始化struct student s {tom, 18, 99.5};给结构体输入数据时只能给到具体的成员变量。1.5 结构体成员的访问使用结构体成员运算符.结构体变量名.结构体成员变量名 s.name // 访问结构体成员 name s.age // 访问结构体成员 age s.score // 访问结构体成员 score1.6 结构体的注意事项1同类型结构体变量之间可以相互赋值struct student s1 {tom, 18, 99.5}; struct student s2 s1; // ✅ 同类型可以直接赋值2结构体作为函数参数时传结构体变量的地址一般不传结构体变量本身因为结构体可能很大传值会复制整个结构体效率低。3通过指针访问结构体成员使用指向结构体成员运算符-用于结构体类型的指针struct student s {tom, 18, 99.5}; struct student *p s; // 以下两种写法等价 p-name // 通过指针访问推荐 (*p).name // 先解引用再用 . 访问1.7 结构体的大小——内存对齐结构体变量定义后需要开辟空间那么结构体的空间有多大这是考点。结构体对齐规则规则一成员的自身对齐每个成员先在自己的自然边界对齐类型对齐要求char任意地址short能被 2 整除的地址int能被 4 整除的地址long能被 4 整除的地址规则二整个结构体的对齐整个结构体的大小必须是最大成员大小的整数倍。struct example { char a; // 1 字节 int b; // 4 字节 char c; // 1 字节 }; // sizeof(struct example) 12而不是 6 // 因为 int 要求 4 字节对齐整个结构体大小要对齐到 4 的倍数二、共用体2.1 什么是共用体共用体union中所有成员共享同一块内存空间。int a; float b; char c; // 这三个变量使用同一块空间2.2 共用体的定义union 共用体名 { 成员1; 成员2; ... };示例union demo { int a; float b; char c; };2.3 共用体的注意事项编号注意点1共用体的大小 最大成员的大小2共用体初始化时只能给一个值这个值给第一个成员3所有成员的起始地址是相同的4如果给成员赋值成员中的值取决于最后赋值后的效果后写的覆盖先写的2.4 共用体的应用判断大小端大端模式数据的高位字节存放在低地址处。小端模式数据的低位字节存放在低地址处。利用共用体可以判断电脑是大端还是小端#include stdio.h int isLittleEndian() { union endian { int a; char c; } u; u.a 1; return u.c; // 如果 c 1说明低位字节在低地址小端 } int main() { if (isLittleEndian()) printf(小端模式\n); else printf(大端模式\n); return 0; }2.5 共用体的实际用途节省空间当一个成员不可能同时出现时如学生有成绩老师有工资但一个人不可能既是学生又是老师可以用共用体节省空间struct member { char name[20]; int age; union { float score; // 学生的成绩 float salary; // 老师的工资 } data; }; struct member s; strcpy(s.name, tom); s.age 20; s.data.score 99.5; // 作为学生使用 score2.6 共用体的使用范围有了共用体类型之后可以像基本类型一样定义共用体变量定义共用体指针变量定义共用体数组作为函数的形参作为函数返回值类型三、枚举3.1 什么是枚举如果一个变量只有几种可能的值则可以定义为枚举类型。所谓枚举是指将变量的值一一列举出来变量的值只限于列举出来的值的范围内。典型场景交通信号灯只有三种状态RED GREEN YELLOW如果用数字表示0代表红灯1代表绿灯...代码中到处都是数字可读性很差——这些数字就叫魔数幻数。枚举可以解决这个问题。3.2 枚举的作用1.提高代码可读性——用有意义的名称代替数字。2.把有关联的数值放到一起——进一步提高代码可读性。3.3 枚举类型的定义enum 枚举名 { 枚举常量1, 枚举常量2, ... };示例enum light { CMD_RED, CMD_GREEN, CMD_YELLOW, };3.4 枚举的注意事项编号注意点1枚举常量的值默认从 0 开始也可以自己指定值。指定了值之后后续常量的值接着这个值依次递增2枚举类型最终实际上是用int型来实现的3枚举类型对比宏定义都能提高代码可读性但枚举是有类型的作用在编译阶段宏定义没有类型作用在预处理阶段enum light { CMD_RED 0, // 默认 0 CMD_GREEN, // 自动为 1 CMD_YELLOW 10, // 手动指定 10 CMD_BLUE, // 自动为 11 };3.5 枚举的使用enum light state CMD_RED; if (state CMD_RED) { printf(红灯停\n); }3.6 练习从键盘输入星期 1 到星期 7打印对应的英文enum week { MON 1, TUE, WED, THU, FRI, SAT, SUN, }; int main() { int day; scanf(%d, day); switch (day) { case MON: printf(Monday\n); break; case TUE: printf(Tuesday\n); break; case WED: printf(Wednesday\n); break; case THU: printf(Thursday\n); break; case FRI: printf(Friday\n); break; case SAT: printf(Saturday\n); break; case SUN: printf(Sunday\n); break; default: printf(输入错误\n); break; } return 0; }四、三种自定义类型对比对比项结构体struct共用体union枚举enum用途将不同类型的数据组合在一起多个成员共享同一块空间变量只有几种固定取值内存各成员各有自己的空间所有成员共享一块空间底层用int实现大小所有成员大小之和含对齐最大成员的大小sizeof(int) 4关键字structunionenum典型场景学生信息、日期、坐标节省内存、大小端判断信号灯、星期、颜色五、总结知识点核心要点结构体定义struct 结构体名 { 成员列表; };先定义类型再定义变量结构体初始化按成员顺序逐个初始化结构体访问.运算符访问成员-运算符通过指针访问成员结构体大小考虑内存对齐成员自身对齐 整体按最大成员对齐结构体赋值同类型结构体变量可直接赋值结构体传参一般传地址指针不传整个结构体变量共用体所有成员共享同一块空间大小为最大成员后赋值的覆盖前面的共用体应用判断大小端、节省空间枚举默认从 0 开始可自定义值底层用 int 实现枚举 vs 宏枚举有类型编译阶段起作用宏没有类型预处理阶段起作用