Appearance
第4章 结构体
🎯 本章学习目标
通过本章的学习,你将能够:
- 理解结构体概念:知道结构体是不同类型数据的集合,与数组的区别
- 正确定义结构体:掌握
struct关键字的使用,记得末尾加分号 - 使用结构体变量:用
.运算符访问成员,进行聚合初始化 - 操作结构体数组:定义、初始化、遍历结构体数组
- 掌握结构体指针:用
->运算符通过指针访问成员 - 理解嵌套结构体:结构体中包含另一个结构体,逐层访问成员
- 结构体传参方式:掌握值传递、指针传递、引用传递三种方式的区别
- 使用const修饰:理解const引用、const指针在结构体中的应用
- 结构体排序:编写自定义比较函数,实现多关键字排序
- 识别常见错误:避免指针/对象运算符混淆、传参方式选择错误等
🗺️ 本章知识图谱
结构体
├── 基础概念
│ ├── 结构体的作用(打包多种类型数据)
│ ├── struct 定义语法
│ ├── 末尾必须加分号
│ └── 结构体 vs 数组
├── 结构体变量
│ ├── 定义结构体变量
│ ├── 聚合初始化(按顺序)
│ ├── . 点运算符访问成员
│ └── 结构体变量赋值
├── 结构体数组
│ ├── 定义与初始化
│ ├── 遍历(双重访问)
│ ├── 查找与统计
│ └── 排序(自定义cmp)
├── 结构体指针
│ ├── 定义与取地址
│ ├── -> 箭头运算符
│ ├── (*p).name 等价于 p->name
│ └── 指针遍历结构体数组
├── 嵌套结构体
│ ├── 结构体包含结构体
│ ├── 逐层访问(. 或 -> 交替)
│ └── 嵌套初始化
├── 结构体传参
│ ├── 值传递(复制,不能修改原数据)
│ ├── 指针传递(传地址,可修改)
│ ├── 引用传递(别名,可修改,推荐)
│ └── const引用(只读,最佳实践)
├── const 与结构体
│ ├── const 结构体变量
│ ├── const 引用参数
│ ├── const 指针参数
│ └── 左定值右定向
└── 结构体排序
├── sort 函数
├── 自定义 cmp 函数
├── 单关键字排序
└── 多关键字排序一、为什么需要结构体?
假设要存储3名学生的信息(姓名、年龄、成绩),只用数组会变成这样:
cpp
string name[3] = {"张三", "李四", "王五"};
int age[3] = {15, 16, 15};
double score[3] = {90.5, 85.0, 95.5};问题很明显:
- 同一个学生的数据被拆散在三个数组中,逻辑关联性差
- 传参时要传三个数组,容易出错
- 新增"学号"字段还得再开一个数组,维护困难
结构体的思路:将不同类型的数据组合成一个有机整体,用一个变量统一管理。
cpp
struct Student {
string name; // 姓名
int age; // 年龄
double score; // 成绩
};
Student students[3]; // 一个数组搞定!💡 类比理解: 数组像一排一模一样的抽屉,每个只能放同一种东西;结构体像一个文件夹,里面可以夹各种不同类型的纸张,用一个夹子统一收纳。
结构体与数组的对比
| 对比维度 | 数组 | 结构体 |
|---|---|---|
| 存储数据类型 | 必须相同 | 可以不同 |
| 访问方式 | 下标 arr[i] | 成员运算符 . 或 -> |
| 典型用途 | 一组同类数据(如10个成绩) | 一个对象的多个属性(如学生信息) |
| 内存布局 | 连续存放,元素大小相同 | 各成员按声明顺序排列,大小可能不同 |
| 可否嵌套 | 多维数组 | 成员可以是另一个结构体 |
二、结构体的定义和使用
2.1 基本语法
cpp
struct 结构体名 {
数据类型 成员变量1;
数据类型 成员变量2;
// ...更多成员
}; // ← 大括号末尾必须有分号!2.2 完整示例
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name; // 姓名
int age; // 年龄
double score; // 成绩
};
int main() {
// 聚合初始化:按成员声明顺序依次赋值
Student s = {"张三", 15, 90.5};
// 使用点运算符访问成员
cout << "姓名:" << s.name << endl;
cout << "年龄:" << s.age << endl;
cout << "成绩:" << s.score << endl;
// 修改成员
s.score = 95.0;
s.age++;
cout << "一年后:" << s.name << " " << s.age << "岁 "
<< s.score << "分" << endl;
return 0;
}
// 输出:
// 姓名:张三
// 年龄:15
// 成绩:90.5
// 一年后:张三 16岁 95分⚠️ 易错点: struct 定义的大括号末尾必须加分号!这是GESP四级高频找错题。
cppstruct Point { int x, y; } // ❌ 编译错误:缺少分号 struct Point { int x, y; }; // ✅ 正确
💡 注意: 结构体定义只是创建了一个类型模板(类似画好一张空表格),并不会分配内存。只有声明变量时才会真正占用内存。
2.3 变量的声明方式
cpp
// 方式一:先定义类型,再声明变量(推荐,最清晰)
struct Student {
string name;
int age;
double score;
};
Student s1, s2;
// 方式二:定义类型的同时声明变量
struct Student2 {
string name;
int age;
} s3, s4;
// 方式三:C++11列表初始化
Student s5 {"李四", 16, 88.0};2.4 聚合初始化的注意事项
cpp
// ✅ 正确:顺序与声明一致
Student s1 = {"张三", 15, 90.5};
// ❌ 错误:顺序写反,90.5给了age,15给了score,数据错乱
Student s2 = {"张三", 90.5, 15};⚠️ 易错点: 聚合初始化的值必须与成员声明的顺序和类型一一对应,写反顺序编译器可能不报错但数据会错乱。
2.5 使用 typedef 简化结构体名
在C语言中,结构体类型名前必须加 struct 关键字,使用 typedef 可以起别名简化书写。C++虽然可以省略 struct,但 typedef 仍然常见且实用:
cpp
#include <iostream>
using namespace std;
// 传统写法:每次声明变量都要写 struct Point
struct Point {
int x, y;
};
// 使用 typedef 起别名
typedef struct {
int x, y;
} Coordinate; // Coordinate 就是这个匿名结构体的别名
int main() {
Point p1 = {1, 2}; // C++中可直接用类型名
//(C语言中需要写 struct Point)
Coordinate p2 = {3, 4}; // 用别名声明,更简洁
cout << "p1: (" << p1.x << ", " << p1.y << ")" << endl;
cout << "p2: (" << p2.x << ", " << p2.y << ")" << endl;
return 0;
}
// 输出:
// p1: (1, 2)
// p2: (3, 4)🔥 高频考点:
typedef的作用是给已有类型起别名,不是创建新类型。考试中可能给出一段使用typedef的代码,要求判断类型或写出输出结果。
三、结构体数组
当需要管理多个同类型结构体数据时,使用结构体数组。
3.1 定义与初始化
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
int main() {
// 定义并初始化结构体数组
Student students[3] = {
{"张三", 15, 90.5},
{"李四", 16, 85.0},
{"王五", 15, 95.5}
};
// 遍历输出
cout << "===== 学生列表 =====" << endl;
for (int i = 0; i < 3; i++) {
cout << i + 1 << ". "
<< students[i].name << " | "
<< students[i].age << "岁 | "
<< students[i].score << "分" << endl;
}
// 查找最高分
int maxIdx = 0;
for (int i = 1; i < 3; i++) {
if (students[i].score > students[maxIdx].score) {
maxIdx = i;
}
}
cout << "\n最高分:" << students[maxIdx].name
<< "(" << students[maxIdx].score << "分)" << endl;
return 0;
}
// 输出:
// ===== 学生列表 =====
// 1. 张三 | 15岁 | 90.5分
// 2. 李四 | 16岁 | 85分
// 3. 王五 | 15岁 | 95.5分
//
// 最高分:王五(95.5分)🔥 高频考点:
students[i].name的访问顺序——先用students[i]取出数组中的结构体元素,再用.name访问成员。考试中常出程序阅读题。
3.2 结构体数组的输入
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
int main() {
int n;
cin >> n;
Student stu[105]; // 最多100名学生
for (int i = 0; i < n; i++) {
cin >> stu[i].name >> stu[i].age >> stu[i].score;
}
// 计算平均分
double sum = 0;
for (int i = 0; i < n; i++) {
sum += stu[i].score;
}
cout << "平均分:" << sum / n << endl;
return 0;
}四、结构体指针
4.1 指针的定义与赋值
cpp
Student s = {"张三", 15, 90.5};
Student* p = &s; // p 指向 s 的地址4.2 成员访问:. 与 -> 运算符
| 运算符 | 适用场景 | 示例 |
|---|---|---|
.(点运算符) | 结构体变量访问成员 | s.name |
->(箭头运算符) | 结构体指针访问成员 | p->name |
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
int main() {
Student s = {"张三", 15, 90.5};
Student* p = &s; // 指针p指向s
// 使用箭头运算符访问成员
cout << "姓名:" << p->name << endl;
cout << "年龄:" << p->age << endl;
// 通过指针修改成员
p->score = 98.0;
cout << "修改后成绩:" << p->score << endl;
// 等价写法:先解引用再用点运算符(必须加括号!)
cout << "等价写法成绩:" << (*p).score << endl;
return 0;
}
// 输出:
// 姓名:张三
// 年龄:15
// 修改后成绩:98
// 等价写法成绩:98⚠️ 易错点(超高频): 变量用
.,指针用->,千万不要混用!
场景 ✅ 正确 ❌ 错误 结构体变量 ss.names->name结构体指针 pp->namep.name记忆口诀:变量点、指针箭。
💡 为什么
(*p).name必须加括号? 因为.的优先级高于*。若写成*p.name,编译器理解为*(p.name)——先访问p.name(指针没有.运算,直接报错),所以必须加括号写成(*p).name。
4.3 结构体指针与数组
结构体数组名本身就是首元素地址,可以直接赋给指针:
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
int main() {
Student stu[3] = {
{"张三", 15, 90.5},
{"李四", 16, 85.0},
{"王五", 15, 95.5}
};
Student* p = stu; // p指向数组首元素
// 方式一:指针偏移遍历
for (int i = 0; i < 3; i++) {
cout << (p + i)->name << " " << (p + i)->score << endl;
}
// 方式二:指针自增遍历
cout << "---" << endl;
for (Student* q = stu; q < stu + 3; q++) {
cout << q->name << " " << q->score << endl;
}
return 0;
}
// 输出:
// 张三 90.5
// 李四 85
// 王五 95.5
// ---
// 张三 90.5
// 李四 85
// 王五 95.5🔥 高频考点:
p + i不是简单地地址加 i 个字节,而是加i * sizeof(Student)个字节,自动跳过一个完整的结构体。这是指针运算的基本规则。
五、结构体嵌套结构体
结构体的成员可以是另一个结构体,形成嵌套。
5.1 基本嵌套
cpp
#include <iostream>
#include <string>
using namespace std;
// 点结构体
struct Point {
int x;
int y;
};
// 矩形:由左上角和右下角两个点确定
struct Rectangle {
Point topLeft; // 左上角(嵌套结构体)
Point bottomRight; // 右下角(嵌套结构体)
string color; // 颜色
};
int main() {
// 嵌套初始化:内层结构体也要用花括号
Rectangle rect = {
{0, 10}, // topLeft
{20, 0}, // bottomRight
"红色" // color
};
// 嵌套访问:逐层使用点运算符
cout << "左上角:(" << rect.topLeft.x << ", "
<< rect.topLeft.y << ")" << endl;
cout << "右下角:(" << rect.bottomRight.x << ", "
<< rect.bottomRight.y << ")" << endl;
cout << "颜色:" << rect.color << endl;
return 0;
}
// 输出:
// 左上角:(0, 10)
// 右下角:(20, 0)
// 颜色:红色5.2 嵌套结构体 + 指针
cpp
#include <iostream>
using namespace std;
struct Point {
int x, y;
};
struct Circle {
Point center; // 圆心
double radius; // 半径
};
int main() {
Circle c = { {3, 4}, 5.0};
Circle* p = &c;
// 第一层是指针用 ->,后续层是变量用 .
cout << "圆心:(" << p->center.x << ", "
<< p->center.y << ")" << endl;
cout << "半径:" << p->radius << endl;
return 0;
}
// 输出:
// 圆心:(3, 4)
// 半径:5🔥 高频考点: 嵌套结构体的成员访问需要逐层使用运算符:
- 变量嵌套:
rect.topLeft.x(每层都是.)- 指针嵌套:
p->center.x(第一层用->,后续层用.)考试常考程序阅读题,让你写出多层嵌套访问的结果,务必细心!
六、结构体做函数参数
结构体作为函数参数有三种传递方式,各有特点,是四级必考内容。
6.1 值传递
将整个结构体复制一份给形参,函数内修改不影响原数据。
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
// 值传递:复制整个结构体
void addScore(Student s, double bonus) {
s.score += bonus; // 只修改了副本
cout << "函数内:" << s.name << " " << s.score << endl;
}
int main() {
Student s = {"张三", 15, 90.5};
addScore(s, 5.0);
cout << "函数外:" << s.name << " " << s.score << endl; // 仍然是90.5
return 0;
}
// 输出:
// 函数内:张三 95.5
// 函数外:张三 90.5⚠️ 易错点: 值传递会复制整个结构体。如果结构体很大(包含大数组等),复制开销很大、效率低。考试中常考"值传递能否修改原数据"——答案是不能。
6.2 指针传递
通过指针传递地址,函数内通过 -> 访问成员,可以修改原数据。
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
// 指针传递
void addScore(Student* p, double bonus) {
p->score += bonus; // 通过指针修改原数据
}
int main() {
Student s = {"张三", 15, 90.5};
addScore(&s, 5.0); // 传入地址
cout << s.name << " " << s.score << endl; // 95.5,已被修改
return 0;
}
// 输出:
// 张三 95.56.3 引用传递
使用引用 &,函数直接操作原数据,不产生副本,语法简洁。
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
// 引用传递:可以修改原数据
void addScore(Student& s, double bonus) {
s.score += bonus;
}
int main() {
Student s = {"张三", 15, 90.5};
addScore(s, 5.0); // 直接传变量,不需要取地址
cout << s.name << " " << s.score << endl; // 95.5
return 0;
}
// 输出:
// 张三 95.56.4 三种传递方式对比
| 传递方式 | 语法 | 是否复制 | 能否修改原数据 | 适用场景 |
|---|---|---|---|---|
| 值传递 | Student s | ✅ 整个结构体 | ❌ 不能 | 小结构体,不需要修改 |
| 指针传递 | Student* p | ❌ 仅复制地址 | ✅ 能 | C风格写法,可为nullptr |
| 引用传递 | Student& s | ❌ 不复制 | ✅ 能 | 需要修改原数据,推荐 |
| const引用 | const Student& s | ❌ 不复制 | ❌ 不能 | 只读访问,最佳实践 |
七、结构体中 const 的使用场景
const 在结构体编程中有三个典型使用场景,是四级考纲明确要求的知识点。
7.1 const 修饰函数参数(只读引用)
当函数只需要读取结构体数据而不需要修改时,使用 const 类型& 既避免拷贝开销,又防止意外修改:
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
// const引用传递:高效且安全
void printStudent(const Student& s) {
cout << s.name << " " << s.age << " " << s.score << endl;
// s.score = 100; // ❌ 编译错误!const限定,不可修改
// s.age++; // ❌ 同样报错
}
int main() {
Student s = {"张三", 15, 90.5};
printStudent(s);
return 0;
}
// 输出:
// 张三 15 90.5🔥 高频考点:
const引用是C++中传递结构体参数的最佳实践——既比值传递高效(不复制),又比普通引用安全(不可修改)。考试中常考"以下哪种参数传递方式最高效且安全"。
7.2 const 修饰结构体指针
指针有两层限定:指针本身能否改变、指针指向的内容能否改变,注意区分:
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int age;
double score;
};
int main() {
Student s1 = {"张三", 15, 90.5};
Student s2 = {"李四", 16, 85.0};
// 1. const 在 * 左边:指向const数据的指针
// 不能通过指针修改所指内容,但指针可以指向别处
const Student* p1 = &s1;
// p1->score = 100; // ❌ 不能修改所指内容
p1 = &s2; // ✅ 指针本身可以改变指向
// 2. const 在 * 右边:const指针
// 指针本身不能改变指向,但可以修改所指内容
Student* const p2 = &s1;
p2->score = 100; // ✅ 可以修改所指内容
// p2 = &s2; // ❌ 指针本身不能改变
// 3. 两边都有 const:既不能改指向,也不能改内容
const Student* const p3 = &s1;
// p3->score = 100; // ❌
// p3 = &s2; // ❌
cout << s1.score << endl; // 100(p2修改了s1)
return 0;
}⚠️ 易错点: 区分
const Student* p和Student* const p——看const在*的哪一侧:
const在*左边 → 数据不可改("指向常量的指针")const在*右边 → 指针不可改("指针常量")记忆口诀:左定值,右定向。
7.3 const 修饰结构体变量 / 成员
cpp
#include <iostream>
using namespace std;
struct Point {
int x;
int y;
};
int main() {
// const结构体变量:所有成员都不可修改
const Point p = {1, 2};
// p.x = 10; // ❌ 编译错误
cout << p.x << " " << p.y << endl; // ✅ 可以读取
// 非const变量可以正常修改
Point q = {3, 4};
q.x = 30;
cout << q.x << " " << q.y << endl;
return 0;
}
// 输出:
// 1 2
// 30 4💡 const 使用场景总结:
- 函数参数:
void func(const Student& s)——只读访问,最高效安全- 函数参数(指针):
void func(const Student* p)——指针版只读访问- 变量声明:
const Student s = {...}——定义后不可修改- 比较函数:
bool cmp(const Student& a, const Student& b)——排序时保证不修改数据
八、📝 实战练习
练习题 1(单选题)
题目: 以下关于结构体的说法,正确的是( )
① 结构体中的各成员数据类型必须相同 ② 结构体定义末尾的分号可以省略 ③ 结构体变量可以使用花括号 {} 进行聚合初始化 ④ const Student* p 表示不能通过指针修改所指向的结构体内容 ⑤ 结构体做函数参数时值传递不会复制数据 A. ①③ B. ②④ C. ③④ D. ③⑤
🔑 点击查看答案与解析
答案: C
解析:
| 说法 | 判断 | 理由 |
|---|---|---|
| ① | ❌ | 结构体成员可以是不同类型,这正是结构体与数组的核心区别 |
| ② | ❌ | struct定义末尾必须加分号,否则编译错误 |
| ③ | ✅ | 聚合初始化是结构体的标准初始化方式,按声明顺序赋值 |
| ④ | ✅ | const 在 * 左边,表示指向const数据的指针,不能修改所指内容 |
| ⑤ | ❌ | 值传递会复制整个结构体,引用和指针传递才不复制 |
故选 C(③④)。
🔥 考点: 结构体基本概念、struct分号、const指针的含义——均为四级高频选择题考点。
练习题 2(程序阅读题)
题目: 阅读以下程序,写出运行结果。
cpp
#include <iostream>
using namespace std;
struct Date {
int year;
int month;
int day;
};
struct Student {
string name;
Date birthday;
int score;
};
int main() {
Student s = {"张三", {2009, 5, 20}, 92};
Student* p = &s;
cout << p->name << endl;
cout << p->birthday.year << "-"
<< p->birthday.month << "-"
<< p->birthday.day << endl;
p->score += 5;
cout << s.score << endl;
return 0;
}🔑 点击查看答案与解析
答案:
张三
2009-5-20
97解析:
p->name:指针用->访问name,输出张三p->birthday.year:第一层指针用->,birthday是Date类型变量(非指针),所以后续用.逐层访问,输出2009-5-20p->score += 5:通过指针修改原数据,原成绩92分加5分变为97,s.score输出97
🔥 考点: 嵌套结构体的指针访问——
p->birthday.year,第一层->后续.,是四级程序阅读题的经典考法。
练习题 3(编程题)
题目: 有 n 名学生,每名学生有姓名、语文成绩、数学成绩。请按总分从高到低排序输出;总分相同时,按语文成绩从高到低排序;语文成绩也相同时,按姓名字典序从小到大排序。
输入格式:
- 第一行一个整数 n(1 ≤ n ≤ 100)
- 接下来 n 行,每行一个字符串和两个整数,分别为姓名、语文成绩、数学成绩
输出格式: n 行,按排序后顺序输出姓名、语文、数学、总分,用空格隔开。
🔑 点击查看答案与解析
参考代码:
cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
struct Student {
string name;
int chinese;
int math;
int total;
};
// 比较函数:参数用const引用,高效且安全
bool cmp(const Student& a, const Student& b) {
if (a.total != b.total)
return a.total > b.total; // 总分降序
if (a.chinese != b.chinese)
return a.chinese > b.chinese; // 语文降序
return a.name < b.name; // 姓名升序(字典序)
}
int main() {
int n;
cin >> n;
Student stu[105];
for (int i = 0; i < n; i++) {
cin >> stu[i].name >> stu[i].chinese >> stu[i].math;
stu[i].total = stu[i].chinese + stu[i].math;
}
sort(stu, stu + n, cmp);
for (int i = 0; i < n; i++) {
cout << stu[i].name << " "
<< stu[i].chinese << " "
<< stu[i].math << " "
<< stu[i].total << endl;
}
return 0;
}输入输出示例:
输入:
4
张三 90 85
李四 85 95
王五 90 90
赵六 85 85
输出:
王五 90 90 180
李四 85 95 180
张三 90 85 175
赵六 85 85 170解析:
- 王五(180)和李四(180)总分相同,语文分别为90和85,王五语文更高排前面
- 张三175分排第三,赵六170分排第四
- 三级排序关键字:总分 → 语文 → 姓名
🔥 考点: 结构体数组 + 多关键字排序 +
const引用比较函数,GESP四级编程题高频题型。
练习题 4(单选题)
题目: 有以下代码,说法正确的是( )
cpp
struct Student { string name; int score; };
void func1(Student s) { s.score = 100; }
void func2(Student& s) { s.score = 100; }
void func3(const Student& s) { cout << s.score; }A. 调用 func1 可以修改原结构体的 score
B. 调用 func2 会复制整个结构体
C. func3 可以读取和修改结构体成员
D. func3 是只读访问,效率高且安全
🔑 点击查看答案与解析
答案: D
解析:
- A 错误:
func1是值传递,形参是副本,修改不影响原数据 - B 错误:
func2是引用传递,不产生副本,直接操作原数据 - C 错误:
func3是 const 引用,只能读取不能修改 - D 正确 ✅:const 引用 = 高效(不拷贝)+ 安全(不能改),是只读访问的最佳实践
💡 记忆口诀:值传递改副本,引用传递改原件,const引用只读不修改。
练习题 5(程序阅读题)
题目: 阅读以下程序,输出结果是( )
cpp
#include <iostream>
using namespace std;
struct Point { int x, y; };
struct Line {
Point start;
Point end;
};
int main() {
Line l = {{1, 2}, {3, 4}};
Line* p = &l;
cout << p->start.x + p->end.y << endl;
return 0;
}A. 3
B. 5
C. 6
D. 7
🔑 点击查看答案与解析
答案: B
解析:
逐层分析:
p->start.x:第一层指针用->,start是 Point 类型变量用.,最后取x= 1p->end.y:第一层指针用->,end是 Point 类型变量用.,最后取y= 4- 1 + 4 = 5
选 B。
🔥 高频考点:嵌套结构体的指针访问——第一层是指针就用 ->,后续都是变量用 .。就像找朋友家:先按门铃(指针),进了门之后直接开门(成员变量)。
练习题 6(判断题)
题目: 结构体数组中每个元素都是独立的结构体变量,它们的成员在内存中是连续存储的。( )
🔑 点击查看答案与解析
答案: 正确 ✅
解析: 结构体数组和普通数组一样,元素在内存中是连续存储的。每个元素是一个结构体,结构体内部的成员按声明顺序依次排列。
🍎 生活比喻:就像一排一模一样的快递柜,每个柜子里都分了好几层(对应结构体的不同成员),柜子之间是紧挨着的。
练习题 7(单选题)
题目: 有定义 const Student* p;,以下说法正确的是( ) A. p 是常量指针,指向不能改变
B. 可以通过 p 修改所指向结构体的成员
C. p 的指向可以改变,但不能通过 p 修改数据
D. p 必须在定义时初始化,之后不能改变指向
🔑 点击查看答案与解析
答案: C
解析:
const Student* p 中 const 在 * 的左边,属于"指向常量的指针":
- 指向的值不能改:不能通过 p 修改所指向结构体的成员
- 指针指向可以改:p 可以改为指向另一个 Student
逐一分析:
- A 错误:常量指针是
Student* const p(const 在 * 右边) - B 错误:const 在左边,数据不可改
- C 正确 ✅:左定值——指向的值不能改,指针本身可以改
- D 错误:这是常量指针(const 在右边)的特点
💡 记忆口诀:左定值,右定向。const 在 * 左边 → 数据不可改;const 在 * 右边 → 指向不可改。
练习题 8(编程题)
题目: 有 n 名学生,每名学生有姓名和成绩。请统计成绩高于平均分的学生人数,并输出他们的姓名和成绩。
输入:第一行 n,接下来 n 行每行姓名和成绩
输出:第一行人数,接下来每行姓名和成绩(按输入顺序)
🔑 点击查看答案与解析
参考答案:
cpp
#include <iostream>
#include <string>
using namespace std;
struct Student {
string name;
int score;
};
int main() {
int n;
cin >> n;
Student stu[105];
int sum = 0;
for (int i = 0; i < n; i++) {
cin >> stu[i].name >> stu[i].score;
sum += stu[i].score;
}
double avg = (double)sum / n; // 计算平均分
int count = 0;
// 先统计人数
for (int i = 0; i < n; i++) {
if (stu[i].score > avg) count++;
}
cout << count << endl;
// 再输出高于平均分的学生
for (int i = 0; i < n; i++) {
if (stu[i].score > avg) {
cout << stu[i].name << " " << stu[i].score << endl;
}
}
return 0;
}解析:
这是结构体数组的经典应用,核心思路:
- 读入所有学生数据,同时累加总分
- 计算平均分 = 总分 / 人数
- 遍历数组,统计成绩高于平均分的人数
- 再次遍历数组,输出满足条件的学生
💡 小技巧:为什么要遍历两次?因为题目要求先输出人数,再输出具体学生。如果想一次遍历完成,可以先把满足条件的学生存到另一个数组里。
九、📊 考点总结表
| 考点 | 重要程度 | 出题形式 | 易错点 |
|---|---|---|---|
| 结构体定义与分号 | ⭐⭐⭐ | 选择/判断/找错 | struct末尾必须加分号,容易忘 |
| 聚合初始化 | ⭐⭐⭐⭐ | 选择/程序阅读 | 按声明顺序赋值,顺序不能乱 |
点运算符 . 访问成员 | ⭐⭐⭐⭐⭐ | 程序阅读/编程 | 对象用 .,指针用 ->,搞混 |
| 结构体数组 | ⭐⭐⭐⭐⭐ | 编程题 | 遍历和访问时的双重下标 |
结构体指针与 -> | ⭐⭐⭐⭐⭐ | 选择/程序阅读 | *p.name 漏括号,应为 (*p).name |
| 嵌套结构体访问 | ⭐⭐⭐⭐ | 程序阅读 | 逐层访问,指针层用->,变量层用. |
| 值/引用/指针传递 | ⭐⭐⭐⭐⭐ | 选择/程序填空 | 值传递不能修改原数据,引用传递可以 |
| const 使用场景 | ⭐⭐⭐⭐ | 选择/判断 | 左定值右定向,容易记混 |
| 结构体排序(自定义cmp) | ⭐⭐⭐⭐⭐ | 编程题 | cmp参数用const引用,返回bool |
| 多关键字排序 | ⭐⭐⭐⭐ | 编程题 | 关键字优先级顺序,升降序搞反 |
⭐ 必背速记卡
┌─────────────────────────────────────────────┐
│ 结构体 · 必背速记卡 │
├─────────────────────────────────────────────┤
│ 1. 结构体打包不同类型数据,数组只能同类型 │
│ 2. struct 定义末尾必须加分号 ; │
│ 3. 对象访问成员用 . 点运算符 │
│ 4. 指针访问成员用 -> 箭头运算符 │
│ 5. p->name 等价于 (*p).name,括号不能漏 │
│ 6. 结构体变量可直接赋值(同类型之间) │
│ 7. 嵌套结构体逐层访问,指针层->变量层. │
│ 8. 值传递:复制整个结构体,不能修改原数据 │
│ 9. 引用传递:不复制,能修改原数据 │
│ 10. const引用:不复制,只读不修改,最安全 │
│ 11. const在*左边 → 数据不可改(左定值) │
│ 12. const在*右边 → 指向不可改(右定向) │
│ 13. 结构体排序用 sort + 自定义 cmp 函数 │
│ 14. cmp 函数参数用 const 引用,返回 bool │
│ 15. 多关键字排序:先比第一关键字,相同比下一个│
└─────────────────────────────────────────────┘