Skip to content

第4章 结构体 ​


🎯 本章学习目标 ​

通过本章的学习,你将能够:

  1. 理解结构体概念:知道结构体是不同类型数据的集合,与数组的区别
  2. 正确定义结构体:掌握 struct 关键字的使用,记得末尾加分号
  3. 使用结构体变量:用 . 运算符访问成员,进行聚合初始化
  4. 操作结构体数组:定义、初始化、遍历结构体数组
  5. 掌握结构体指针:用 -> 运算符通过指针访问成员
  6. 理解嵌套结构体:结构体中包含另一个结构体,逐层访问成员
  7. 结构体传参方式:掌握值传递、指针传递、引用传递三种方式的区别
  8. 使用const修饰:理解const引用、const指针在结构体中的应用
  9. 结构体排序:编写自定义比较函数,实现多关键字排序
  10. 识别常见错误:避免指针/对象运算符混淆、传参方式选择错误等

🗺️ 本章知识图谱 ​

结构体
├── 基础概念
│   ├── 结构体的作用(打包多种类型数据)
│   ├── 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四级高频找错题。

cpp
struct 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.5

6.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.5

6.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 使用场景总结:

  1. 函数参数:void func(const Student& s) ——只读访问,最高效安全
  2. 函数参数(指针):void func(const Student* p) ——指针版只读访问
  3. 变量声明:const Student s = {...} ——定义后不可修改
  4. 比较函数: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

解析:

  1. p->name:指针用 -> 访问 name,输出 张三
  2. p->birthday.year:第一层指针用 ->,birthday 是 Date 类型变量(非指针),所以后续用 . 逐层访问,输出 2009-5-20
  3. p->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 = 1
  • p->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;
}

解析:

这是结构体数组的经典应用,核心思路:

  1. 读入所有学生数据,同时累加总分
  2. 计算平均分 = 总分 / 人数
  3. 遍历数组,统计成绩高于平均分的人数
  4. 再次遍历数组,输出满足条件的学生

💡 小技巧:为什么要遍历两次?因为题目要求先输出人数,再输出具体学生。如果想一次遍历完成,可以先把满足条件的学生存到另一个数组里。


九、📊 考点总结表 ​

考点重要程度出题形式易错点
结构体定义与分号⭐⭐⭐选择/判断/找错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. 多关键字排序:先比第一关键字,相同比下一个│
└─────────────────────────────────────────────┘

百炼成钢,融会贯通