结构体与联合体
一、课上练习
编程练习
二、知识总结
✨ 结构体与联合体的核心思想
在 C++ 中,结构体(struct) 是一种用户自定义的数据类型,可以将不同类型的数据组合在一起。
结构体提供了一种方便的方法来处理与现实实体相关的数据,例如学生、书籍、坐标点等。结构体可以包含成员变量(数据) 和成员函数(方法),虽然与类(class)类似,但结构体通常主要用于简单的数据聚合,并且在竞赛中我们通常不会使用成员函数。
✨ 定义结构体的方法
结构体的定义使用 struct 关键字,后跟结构体的名称和一组包含在大括号 {} 中的成员变量,以分号结尾。
定义结构体时需要注意以下几点:
- 结构体名称一般使用首字母大写的方式来命名
- 成员变量代表具体要存储的内容,可以是任何基本数据类型,也可以是其他结构体
- 结构体的定义必须要以分号结尾
- 一般把结构体的定义写在主函数外面
下面分别展示定义只包含基础数据类型、包含数组、以及包含结构体数组的结构体:
1#include <iostream>
2using namespace std;
3
4struct Person{
5 string name; // 姓名
6 int age; // 年龄
7 double height; // 身高
8 double weight; // 体重
9};1#include <iostream>
2using namespace std;
3
4struct Transcript{
5 string class_id; // 班级编号
6 int scores[100]; // 成绩数组
7};1#include <iostream>
2using namespace std;
3
4struct Person{
5 string name; // 姓名
6 int age; // 年龄
7 double height; // 身高
8 double weight; // 体重
9};
10
11struct Group{
12 int group_id;
13 Person persons[100];
14};✨ 结构体使用
结构体是一种自定义的数据类型,因此我们需要把结构体当做数据类型来使用即可。获取结构体变量中的成员变量时,需要使用 . 运算符。
结构体变量
使用自定义的结构体定义变量与定义基础数据类型的变量没有区别,仅需要将基础数据类型的关键字改为自定义的结构体名称即可。
下面展示两种定义和初始化结构体变量的方式:
1#include <iostream>
2using namespace std;
3
4struct Point {
5 int x;
6 int y;
7};
8
9int main() {
10 // 定义Point类型的变量p1,使用.的方式对其进行赋值。
11 Point p1;
12 p1.x = 3;
13 p1.y = 6;
14
15 // 定义Point类型的变量p2,使用初始化列表的方式对其成员变量进行初始化
16 // 此方法需要注意:仅可以对基础数据类型的成员变量进行初始化
17 Point p2 = {10, 20}; // 列表初始化
18
19 cout << "Point p1: (" << p1.x << ", " << p1.y << ")" << endl;
20 cout << "Point p2: (" << p2.x << ", " << p2.y << ")" << endl;
21
22 return 0;
23}1#include <iostream>
2using namespace std;
3
4// 定义Point类型结构体
5// 定义Point类型的变量p1,不对p1进行初始化
6// 定义Point类型的变量p2,使用列表的方式对p2进行初始化
7// 在此处定义的p1和p2是全局变量
8struct Point {
9 int x;
10 int y;
11}p1, p2{4, 8};
12
13int main() {
14 // 主函数内可以直接使用p1和p2
15 p1.x = 3;
16 p1.y = 6;
17
18 cout << "Point p1: (" << p1.x << ", " << p1.y << ")" << endl;
19 cout << "Point p2: (" << p2.x << ", " << p2.y << ")" << endl;
20
21 return 0;
22}结构体数组
使用自定义的结构体定义数组与定义基础数据类型的数组没有区别,仅需要将基础数据类型的关键字改为自定义的结构体名称即可。
1#include <iostream>
2using namespace std;
3
4struct Point {
5 int x;
6 int y;
7};
8
9// 定义Point类型的数组p
10Point p[1000];
11
12int main() {
13 // 对结构体数组p进行赋值
14 for (int i = 0; i < 1000; ++i) {
15 p[i].x = i;
16 p[i].y = i;
17 }
18
19 return 0;
20}结构体与函数
可以把自定义结构体类型的变量当做参数传递给函数,也可以让一个函数返回一个自定义结构体类型的数据。
1#include <iostream>
2using namespace std;
3
4struct Point {
5 int x;
6 int y;
7};
8
9// 输出一个Point类型的变量的成员变量
10void printPoint(Point p) {
11 cout << "Point: (" << p.x << ", " << p.y << ")" << endl;
12}
13
14// 创建一个Point类型的变量
15Point createPoint(int x, int y) {
16 Point p;
17 p.x = x;
18 p.y = y;
19 return p;
20}
21
22int main() {
23 Point p1 = createPoint(10, 20);
24 printPoint(p1);
25
26 return 0;
27}✨ 联合体概念
在 C++ 中,联合体(union) 是一种用户自定义的数据类型,类似于结构体(struct),但具有不同的内存管理方式。联合体中的所有成员共享同一块内存,意味着在任一时刻,联合体只能存储其中一个成员的值。联合体主要用于需要高效地使用内存的场景。
联合体通常用于以下场景:
- 节省内存空间:在需要节省内存的地方,联合体可以高效地存储不同类型的数据
- 处理多种数据类型:在需要处理多种数据类型但同一时刻只需要一种的场景,联合体是一个有效的解决方案
✨ 定义联合体
联合体的定义使用 union 关键字,后跟联合体的名称和一组包含在大括号 {} 中的成员变量,以分号结尾。
定义联合体时需要注意以下几点:
- 联合体名称一般使用首字母大写的方式来命名
- 成员变量代表具体要存储的内容,可以是任何基本数据类型
- 联合体的定义必须要以分号结尾
- 一般把联合体的定义写在主函数外面
1#include <iostream>
2using namespace std;
3
4union Data {
5 int i;
6 float f;
7 char c;
8};
9
10int main() {
11 Data data;
12
13 data.i = 10;
14 cout << "data.i: " << data.i << endl;
15
16 data.f = 3.14;
17 cout << "data.f: " << data.f << endl;
18
19 data.c = 'a';
20 cout << "data.c: " << data.c << endl;
21
22 // 注意:此时访问其他成员将得到未定义的结果
23 cout << "data.i: " << data.i << endl;
24
25 return 0;
26}✨ 联合体的使用
联合体的使用方式和结构体基本一致,可以使用初始化列表对联合体变量进行初始化。
1#include <iostream>
2using namespace std;
3
4union Data {
5 int i;
6 float f;
7 char c;
8};
9
10int main() {
11 Data data = {10}; // 初始化整数成员
12
13 cout << "data.i: " << data.i << endl;
14
15 data = {3.14f}; // 初始化浮点数成员
16 cout << "data.f: " << data.f << endl;
17
18 data = {'a'}; // 初始化字符成员
19 cout << "data.c: " << data.c << endl;
20
21 return 0;
22}✨ 匿名联合体
在某些情况下,联合体可以是匿名的,即没有名称的联合体。匿名联合体的成员直接属于包含它的作用域,可以在结构体内部使用。
1#include <iostream>
2using namespace std;
3
4struct Test {
5 union {
6 int i;
7 float f;
8 };
9
10 void print() {
11 cout << "i: " << i << ", f: " << f << endl;
12 }
13};
14
15int main() {
16 Test t;
17 t.i = 10;
18 t.print();
19
20 t.f = 3.14f;
21 t.print();
22
23 return 0;
24}✨ 结构体与联合体的执行示例
下面通过具体的内存示意图,展示结构体和联合体的工作方式。
例1:结构体的内存布局
1struct Student {
2 char name[10]; // 占10字节
3 int age; // 占4字节
4 double score; // 占8字节
5};
6
7Student s = {"Tom", 15, 92.5};结构体各成员在内存中依次排列,每个成员拥有独立的存储空间:
1内存地址(示意):
2+----------+------+----------+
3| name | age | score |
4| "Tom" | 15 | 92.5 |
5| [10字节] |[4字节]| [8字节] |
6+----------+------+----------+结构体的总大小 >= 所有成员大小之和(实际可能因内存对齐而更大)。
例2:联合体的内存布局
1union Data {
2 int i; // 占4字节
3 float f; // 占4字节
4 char c; // 占1字节
5};
6
7Data d;联合体所有成员共享同一块内存,大小等于最大成员的大小:
内存地址(示意):
+----------------+
| i / f / c |
| 共享 [4字节] |
+----------------+d.i = 10; → 内存中存储整数10
d.f = 3.14; → 内存被覆盖为浮点数3.14,此时d.i的值已经无意义
d.c = 'A'; → 内存被覆盖为字符'A',此时d.i和d.f的值已经无意义例3:结构体数组的定义、赋值与排序
1struct Student {
2 string name;
3 int score;
4};
5
6Student stu[3];
7
8// 读入3个学生信息
9for (int i = 0; i < 3; ++i) {
10 cin >> stu[i].name >> stu[i].score;
11}假设输入为 Alice 85、Bob 92、Charlie 78,内存中的存储状态:
stu[0]: name="Alice", score=85
stu[1]: name="Bob", score=92
stu[2]: name="Charlie", score=78配合 sort 按成绩降序排序后:
stu[0]: name="Bob", score=92
stu[1]: name="Alice", score=85
stu[2]: name="Charlie", score=78✨ 结构体与联合体的解题步骤
使用结构体解决编程问题的一般步骤:
- 分析数据:确定需要存储哪些信息,每条信息包含哪些字段(例如学生有姓名、年龄、成绩等)
- 定义结构体:在主函数外面用
struct定义结构体,包含所有需要的成员变量 - 定义结构体数组:根据数据量定义足够大的结构体数组
- 读入数据:使用循环和
.运算符逐个读入每个结构体的成员变量 - 处理数据:根据题目要求进行计算、排序等操作(排序时需要编写 cmp 函数)
- 输出结果:使用
.运算符访问成员变量并输出
典型应用场景示例——按要求排序学生信息:
1#include <bits/stdc++.h>
2using namespace std;
3
4struct Student {
5 string name;
6 int score;
7};
8
9bool cmp(Student a, Student b) {
10 return a.score > b.score; // 按成绩降序排列
11}
12
13Student stu[1005];
14
15int main() {
16 int n;
17 cin >> n;
18 for (int i = 0; i < n; ++i) {
19 cin >> stu[i].name >> stu[i].score;
20 }
21 sort(stu, stu + n, cmp);
22 for (int i = 0; i < n; ++i) {
23 cout << stu[i].name << " " << stu[i].score << endl;
24 }
25 return 0;
26}✨ 结构体与联合体的常见错误
- 定义结构体时忘记末尾的分号:
struct Point { int x; int y; }后面必须加;,否则编译报错 - 在主函数内部定义结构体:结构体应该定义在主函数外面(全局位置),否则无法在其他函数中使用
- 混淆
.和->:使用结构体变量访问成员用.(如stu.name),使用结构体指针访问成员用->(如ptr->name)。在本阶段主要使用.运算符 - 联合体中同时使用多个成员:联合体同一时刻只能存储一个成员的值,给一个成员赋值后再读取其他成员会得到无意义的结果
- 结构体数组开在主函数内部导致栈溢出:当结构体数组较大时(如超过 10000 个元素),应该定义为全局变量,否则可能因栈空间不足导致程序崩溃
✨ 联合体注意事项
使用联合体时需要特别注意以下两点:
- 同一时刻只能存储一个成员的值:因为所有成员共享同一块内存,同时只能有一个成员存储有效数据
- 避免访问未定义行为:如果在给某个成员赋值后访问其他成员,会产生未定义行为,因为这些成员的值会被覆盖
三、课后练习
基础知识练习
- 结构体与联合体 - 选择题## 编程练习