← 课程目录
第 19 讲 模块六:进阶与综合

struct:把相关数据打包

19 / 25
💡
本节课目标
进入最后一个模块。今天学 struct(结构体)——把姓名、分数这些 属于同一个人的数据打包成一个整体,用点号访问其中的成员, 再把它和 vector 组合起来,管理一整个班的学生。
开场

姓名和分数,各存各的?

📖 理论讲解

上个模块留下的别扭

第 18 讲的成绩统计程序,只存了分数。如果还想记住每个分数是的, 按现在的办法,只能开两个 vector

parallel_vectors.cpp CPP
std::vector<std::string> names;   // 姓名放这里
std::vector<int> scores;          // 分数放那里

// names[2] 和 scores[2] 是同一个人——全靠下标对齐,全靠自觉
// 万一某次只往其中一个 push_back,两个 vector 就错位了
⚠️
靠下标对齐,是很脆弱的约定
这两个 vector 在语法上毫无关系, “第 2 个名字和第 2 个分数是同一个人”这件事,编译器根本不知道,全靠你自己记牢。 一旦某处只更新了一个、漏了另一个,数据就错位了, 而且这种 bug 极难发现——程序照样能跑,只是每个人的分数都张冠李戴。
📖 理论讲解

struct:让相关的数据长在一起

更合理的想法是:既然姓名和分数本来就属于同一个学生, 那就应该把它们绑成一个东西,而不是拆成两半分开放。 struct 做的就是这件事——把几个相关的变量,打包成一个新的类型

第一段

定义一个 struct

📖 理论讲解

语法:struct 类型名 { 成员们; };

struct_define.cpp CPP
struct Student {
    std::string name;
    int score;
};   // 注意:这里的分号不能少
部分例子含义
structstruct固定关键字,表示要定义结构体
类型名Student你给这个新类型起的名字
成员namescore打包进来的那些变量
🚫
右大括号后面的分号,千万别漏
}; ——结构体定义的末尾必须有分号。 这和函数、iffor 的大括号都不一样(那些后面不加分号), 是初学 struct最高频的编译错误
💡
定义类型 ≠ 创建变量
struct Student { … }; 只是设计了一张图纸, 告诉编译器“以后有一种东西叫 Student,它由姓名和分数组成”—— 这时还没有任何学生存在。真正造出一个学生,要等到下面 Student s; 这一步。
第二段

创建变量,用点号访问成员

📖 理论讲解

像用 int 一样用 Student

struct 定义好之后,Student 就成了一个intdouble 地位相同的类型,可以直接拿来声明变量。 访问里面的成员,用点号 .

struct_use.cpp CPP
#include <iostream>
#include <string>

struct Student {
    std::string name;
    int score;
};

int main() {
    Student s;            // 声明一个 Student 变量
    s.name = "小明";      // 用点号访问成员
    s.score = 90;

    std::cout << s.name << " 的分数是 " << s.score << std::endl;
    return 0;
}
s.name 就是一个普通变量
s.namestd::strings.scoreint—— 它们能做的事,和普通的 stringint 变量完全一样: 能赋值、能 cin 读入、能参与运算、能拿去 if 判断。 唯一的区别只是名字前面多了 s. 这个前缀。
📖 理论讲解

也可以声明时一次性赋值

和数组的初始化列表很像,用花括号按成员声明的顺序依次给值:

struct_init.cpp CPP
Student s = {"小明", 90};   // 按声明顺序,一次性给全部成员赋值

// 等价于:
// Student s;
// s.name = "小明";
// s.score = 90;
⚡ 动手实操 约 10 分钟

定义你自己的 struct

仿照 struct_use.cpp,定义一个 struct Book, 包含三个成员:书名(std::string title)、作者 (std::string author)、页数(int pages)。

  1. main 里创建一本书,给三个成员各赋一个值。
  2. 把这本书的信息完整打印出来。
  3. 再试试用花括号一次性赋值的写法,确认效果一样。
第三段

struct + vector:管理一整个班

📖 理论讲解

vector 里装什么类型都行

第 16 讲说过,std::vector<类型> 尖括号里写要装的元素类型。 既然 Student 现在也是一个正经类型,那当然可以写 std::vector<Student>——一个装学生的 vector

struct_vector.cpp CPP
#include <iostream>
#include <string>
#include <vector>

struct Student {
    std::string name;
    int score;
};

int main() {
    std::vector<Student> students;   // 一个装 Student 的 vector

    students.push_back({"小明", 90});
    students.push_back({"小红", 85});
    students.push_back({"小刚", 78});

    for (int i = 0; i < students.size(); i++) {
        std::cout << students[i].name << ":" << students[i].score << std::endl;
    }
    return 0;
}
students[i].name 怎么读
从左往右拆:students[i] 先取出第 i 个学生 (这是一个完整的 Student),再 .name 取出这个学生的姓名。 先选人,再选属性。
💡
现在数据不会错位了
对比开头的两个 vector:姓名和分数现在长在同一个 Student 里面push_back 一次加入一个 Student,姓名和分数会作为同一条记录一起移动, 不再出现两个容器长度不同造成的错位——用类型固定数据之间的归属关系, 比“靠自己记得对齐”可靠得多。
📖 理论讲解

配合 cin 逐个录入

实际程序里,学生信息往往是用户输入的。先填好一个 Student 变量, 再整个 push_back 进去:

struct_input.cpp CPP
Student s;
std::cout << "姓名:";
std::cin >> s.name;
std::cout << "分数:";
std::cin >> s.score;

students.push_back(s);   // 填好一个,整个塞进 vector
实战

动手写三个结构体程序

练习 19-1

一本书的信息

定义 struct Book(书名、作者、页数),在 main 里创建两本书并分别打印它们的完整信息。

练习 19-2

录入并遍历学生

先输入班级人数,再用循环逐个录入每个学生的姓名和分数,存进 std::vector<Student>,最后遍历打印所有学生。

提示:struct_input.cpp 的录入逻辑放进 第 18 讲那样的 for 循环里。

练习 19-3

找出分数最高的学生

在练习 19-2 的基础上,用第 15 讲的打擂台算法, 找出分数最高的学生,并打印他的姓名和分数

💡
想一想
擂主变量该声明成什么类型?如果只用 int maxScore 记住最高分, 最后还能知道这个分数是的吗?试试直接用一个 Student 当擂主,比较时比 .score
收尾

小结

📖 理论讲解

这节课你掌握了

能力用到的工具
把相关数据打包成新类型struct 类型名 { 成员; };
创建结构体变量Student s;Student s = {…};
访问某个成员点号 s.names.score
批量管理一组对象std::vector<Student>

最该记住:数据之间有关系,就让类型体现这种关系—— 别用几个平行的 vector 加“自觉对齐”来凑合。

💡
下节课预告
现在录入、遍历、统计的代码,又全都堆在 main 里了—— 这正是第 11 讲开头抱怨过的问题。下节课把这些操作封装进函数, 学会怎么把 vector 传给函数,让代码结构真正清爽起来。