知识点#
10.1 结构体变量定义、引用与初始化#
- @ 结构体基础图
- ![[白板/P29 结构体基础.canvas|P29 结构体基础]]
10.1.1 结构体简介#
[!tip] 结构体
- @ 结构体: 把多种不同类型的数据组合在一起构成的新数据类型
- 目的: 表达更丰富、更全面的信息
- 结构体中的每个数据称为一个==结构体成员==
- ! 结构体是 C++ 中"类"概念的基础
struct student { // 定义一个结构体类型
int num; // 学号
char name[100]; // 姓名
int sex; // 性别 0女, 1男
int age; // 年龄
char address[100]; // 地址
}; // 这里的分号不要忘记!
- 一般形式:
struct 结构体名 {
成员列表
};
10.1.2 定义结构体类型变量的方法#
方法一: 先定义类型,再定义变量#
struct student s1, s2; // 定义两个结构体变量
方法二: 定义类型的同时定义变量#
struct student {
int num;
char name[100];
int sex;
int age;
char address[100];
} s1, s2; // 同时定义变量,多个变量用逗号分隔
方法三: 直接定义结构体类型变量(省略结构体名)#
struct {
int num;
char name[100];
int sex;
} s1, s2; // 省略了结构体名
结构体嵌套#
struct date { // 定义一个结构体类型
int month;
int day;
int year;
};
struct student {
int num;
char name[100];
int sex;
int age;
char address[100];
struct date birthday; // 生日,是一个结构体成员
};
- ! 结构体内成员名可以与程序中的变量名相同,互不影响
10.1.3 结构体类型变量的引用#
[!tip] 结构体成员运算符
- @
.: 结构体成员运算符,优先级非常高,与圆括号()平级- 引用方式:
结构体变量名.成员名
引用规则#
- ! 不能将结构体变量作为整体引用,只能对各个成员分别引用
- ! 嵌套结构体要一级一级找到最低级成员
s1.num = 1001; // 直接赋值
s1.birthday.month = 12; // 嵌套结构体要逐级引用
s1.birthday.day = 30;
s1.birthday.year = 2018;
// 成员变量可以像普通变量一样运算
s2.age = s1.age; // 赋值
int agesum = s1.age + s2.age; // 求和
s1.age++; // 自增(.优先级最高,s1.age是整体)
// 成员变量有地址
int *p = &s1.num;
printf("%d\n", *p);
10.1.4 结构体变量的初始化#
struct student s5 = {100, "王五", 1, 16, "3栋5楼", 10, 14, 2018};
10.2 结构体数组与结构体指针#
- @ 结构体数组与指针图
- ![[白板/P30 结构体数组与指针.canvas|P30 结构体数组与指针]]
10.2.1 结构体数组#
[!tip] 结构体数组
- 每个数组元素都是一个结构体变量
- 可以存放多组数据
定义方式#
// 方式1: 单独定义
struct student stuArr[3]; // 可用下标 0-2
// 方式2: 定义结构体时顺便定义
struct student {
int num;
// ...
} stuArr[3];
初始化#
struct student stuArr[3] = {
{1001, "张三", 1, 18, "1栋1单元", 12, 30, 2000},
{1002, "李四", 1, 20, "2栋2单元", 11, 15, 1998},
{1003, "王五", 1, 22, "3栋3单元", 10, 15, 1996}
};
// 不指定元素个数,系统自动推断
struct student stuArr[] = {
{1001, "张三", 1, 18, "1栋1单元", 12, 30, 2000},
{1002, "李四", 1, 20, "2栋2单元", 11, 15, 1998},
{1003, "王五", 1, 22, "3栋3单元", 10, 15, 1996}
};
使用#
stuArr[1].age++; // 年龄+1
printf("name = %s\n", stuArr[1].name); // name = 李四
printf("age = %d\n", stuArr[1].age); // age = 21
10.2.2 结构体指针#
[!tip] 结构体指针
- 指向结构体变量所占据的内存起始地址
- 也可指向结构体数组中的元素
struct student stu; // 结构体变量
struct student *ps; // 结构体指针
ps = &stu;
strcpy(stu.name, "小虎");
stu.age = 16;
通过指针访问成员的两种方式#
| 方式 | 写法 | 说明 |
|---|---|---|
| 方式1 | (*ps).num | *ps 要加括号,因为 . 优先级高于 * |
| 方式2 | ps->num | -> 指向结构体成员运算符,优先级最高 |
(*ps).num = 1008; // 第一种
ps->sex = 1; // 第二种(推荐)
printf("name: %s\n", ps->name); // name: 小虎
printf("age = %d\n", ps->age); // age = 16
printf("num = %d\n", ps->num); // num = 1008
结构体指针与数组#
struct student stuArr[3] = { ... };
struct student *ps;
ps = stuArr; // 数组名作为首地址
for(int i = 0; i < 3; i++) {
printf("name = %s\n", ps->name);
printf("age = %d\n", ps->age);
printf("num = %d\n", ps->num);
ps++; // 跳过一个数组元素所占的字节数
}
int ilen = sizeof(struct student);
printf("ilen = %d\n", ilen); // 如 224
指针自增运算#
ps = stuArr;
printf("%d\n", (++ps)->num); // 1002,先++后用
ps = stuArr;
printf("%d\n", (ps++)->num); // 1001,先用后++
- ! 结构体指针只能指向同类型的结构体变量或数组元素,不能指向成员
// 错误写法
ps = &stu.num; // 不能指向结构体变量的成员
ps = &stuArr[0].num; // 不能指向数组元素的成员
10.2.3 用指向结构体的指针作为函数参数#
[!tip] 传参方式对比
方式 效果 效率 传结构体指针 可修改原数据 高(只传地址) 传结构体变量 不能修改原数据 低(复制全部成员)
// 方式1: 传指针(推荐)
void func1(struct student *pd) {
pd->age = 118; // 修改原数据
}
int main() {
struct student *ps = stuArr;
func1(ps); // 传指针
printf("stuArr[0].age = %d\n", stuArr[0].age); // 118,已修改
return 0;
}
// 方式2: 传整个结构体变量(不推荐)
void func1(struct student d) {
d.age = 118; // 只修改形参副本,不影响原数据
}
int main() {
stuArr[0].age = 12;
func1(stuArr[0]); // 传整个变量
printf("stuArr[0].age = %d\n", stuArr[0].age); // 12,未修改
return 0;
}
- ! 传整个结构体变量开销大(所有成员数据全部复制),建议用指针
10.3 共用体、枚举类型与 typedef#
- @ 共用体枚举typedef图
- ![[白板/P31 共用体枚举typedef.canvas|P31 共用体枚举typedef]]
10.3.1 共用体#
[!tip] 共用体(联合)
- @ 共用体: 几种不同类型的变量存放到同一段内存单元
- 成员共用同一段内存,会互相覆盖
- ! 共用体大小 = 占用内存最大的成员的大小
union myun {
int carnum; // 轿车编号,4字节
char cartype; // 轿车类型,1字节
char cname[60]; // 轿车名,60字节
} a, b, c;
// 也可以分开定义
union myun a, b, c;
// 也可以省略共用体名
union {
int carnum;
char cartype;
char cname[60];
} a, b, c;
共用体 vs 结构体#
| 对比项 | 结构体 | 共用体 |
|---|---|---|
| 内存大小 | 各成员大小之和(可能因对齐更多) | 最大成员的大小 |
| 成员关系 | 各占不同内存,互不影响 | 共用同一段内存,互相覆盖 |
| 同时有效 | 所有成员同时有效 | 每一瞬间只有一个成员有效 |
共用体要点#
- ! 每一瞬间只能有一个成员起作用,最后赋值的成员起作用
- ! 共用体变量地址和其所有成员的地址都相同
- ! 不能在定义时给所有成员初始化,只能初始化第一个成员
union myun a;
a.carnum = 128924898;
strcpy(a.cname, "小汽车"); // cname起作用,carnum的值已无意义
// 初始化第一个成员是允许的
union myun b = {12}; // 正确
// 不能初始化所有成员
// union myun a = {12, 'A', "小汽车"}; // 报错!
10.3.2 枚举类型#
[!tip] 枚举类型
- @ 枚举: 将值一一列举出来,用有意义的名字代替数字
- 枚举常量默认从
0开始递增
enum color { // color 是枚举类型名
Red, // 0
Green, // 1
Blue, // 2
Yellow // 3
};
enum color mycolor1, mycolor2; // 定义枚举变量
mycolor1 = Red; // 赋值
// 也可以省略枚举类型名
enum {Red, Green, Blue, Yellow} mycolor1, mycolor2;
枚举要点#
- % 枚举常量是常量,不是变量,不能对它们赋值
- % 默认值从
0开始递增 - % 可以改变默认值
enum color {
Red = 1, // 1
Green, // 2(前一个+1)
Blue = 5, // 5
Yellow // 6(前一个+1)
};
printf("Red = %d\n", Red); // 1
printf("Yellow = %d\n", Yellow); // 6
10.3.3 typedef 关键字#
[!tip] typedef
- @
typedef: 为已有的数据类型定义一个新的名字(别名)- !
typedef不创建新类型,只是给已有类型起别名
// 给 int 起别名
typedef int INTEGER;
INTEGER a, b; // 等价于 int a, b;
// 给结构体起别名(最常用)
typedef struct student {
int num;
char name[100];
int age;
} STU;
STU s1, s2; // 不用写 struct student,直接用 STU
// 给指针类型起别名
typedef int *PINT;
PINT p1, p2; // 等价于 int *p1, *p2;
// 给数组类型起别名
typedef int ARR[10];
ARR a, b; // 等价于 int a[10], b[10];
typedef 与 #define 的区别#
| 对比项 | typedef | #define |
|---|---|---|
| 处理时机 | 编译时 | 预处理时 |
| 类型检查 | 有 | 无 |
| 作用范围 | 受作用域限制 | 从定义到文件末尾 |
| 本质 | 类型别名 | 文本替换 |
// typedef 正确处理指针类型
typedef int *PINT;
PINT a, b; // a 和 b 都是 int*
// #define 可能出错
#define PINT int*
PINT a, b; // a 是 int*,b 是 int!(宏展开为 int* a, b;)

