Appearance
第1章 GESP 四级考点总览
🎯 本章学习目标
通过本章的学习,你将能够:
- 建立全局视野:清晰了解 GESP 四级考试的 8 大知识模块及其重要程度
- 掌握指针核心:理解指针的本质是内存地址,熟练使用
&取地址和*解引用 - 玩转二维数组:掌握二维数组的定义、初始化、遍历和行优先存储规律
- 组织复杂数据:用结构体封装多种类型数据,掌握结构体数组、指针和嵌套
- 精通函数进阶:理解值传递与引用传递的区别,掌握函数声明与作用域规则
- 推导递推关系:从问题描述中找出递推关系式,用递推算法求解经典问题
- 手写三大排序:熟练编写冒泡、插入、选择排序代码,分析复杂度与稳定性
- 操作文件数据:使用
freopen或文件流进行文件的读写操作 - 理解异常机制:掌握 try-throw-catch 的执行流程,能阅读异常处理代码
- 制定备考策略:根据各模块考频和自身情况,合理安排复习计划
🗺️ 本章知识图谱
GESP 四级知识体系
├── 基础语法进阶
│ ├── 指针与内存地址
│ │ ├── 指针定义与初始化
│ │ ├── & 取地址与 * 解引用
│ │ ├── 指针赋值与接力
│ │ ├── 指针与数组
│ │ ├── 指针算术运算
│ │ └── const 与指针
│ ├── 二维及多维数组
│ │ ├── 定义与初始化
│ │ ├── 行优先存储
│ │ ├── 按行/按列遍历
│ │ └── 二维数组做函数参数
│ └── 结构体
│ ├── struct 定义与成员访问
│ ├── 结构体数组
│ ├── 结构体指针与 -> 运算符
│ ├── 结构体嵌套
│ ├── 结构体传参
│ └── const 修饰结构体
├── 函数与作用域
│ ├── 函数定义、调用与声明
│ ├── 形参与实参
│ ├── 值传递 vs 引用传递
│ ├── 全局变量与局部变量
│ └── 作用域与变量遮蔽
├── 算法基础
│ ├── 递推算法
│ │ ├── 递推思想与递推式
│ │ ├── 初始条件与边界
│ │ ├── 斐波那契数列
│ │ └── 经典递推模型
│ └── 排序算法
│ ├── 冒泡排序
│ ├── 插入排序
│ ├── 选择排序
│ ├── 时间/空间复杂度
│ └── 算法稳定性
├── 实用技术
│ ├── 文件操作
│ │ ├── freopen 重定向
│ │ ├── ifstream / ofstream
│ │ ├── 文件打开模式
│ │ └── 文件关闭
│ └── 异常处理
│ ├── try-throw-catch
│ ├── 异常抛出与捕获
│ ├── 多重 catch 块
│ └── 执行流程
└── 备考策略
├── 核心考点优先级
├── 选择题/判断题复习方法
├── 编程题训练技巧
└── 真题使用指南一、写在前面
CCF GESP(编程能力等级认证)四级,是从三级"基础语法"迈向五级"算法与数据结构"的关键过渡台阶。四级考纲正式引入了指针、结构体、函数进阶、递推与排序等核心内容,这些不仅是四级的考试重点,更是后续五级、六级乃至更高级别的编程基石。
💡 本章定位:本章是四级考点总览,先建立全局视野;后续各章将逐模块深入拆解,覆盖知识点精讲、典型例题、编程实战与备考策略。
二、📊 八大考点全景总表
根据 CCF 官方考纲,GESP 四级涵盖 8 大知识模块,一览全局:
| 序号 | 📌 考点模块 | 📖 一句话概括 | ⭐ 考频星级 |
|---|---|---|---|
| ① | 指针 | 指针的定义、赋值与解引用——C++灵魂语法,选择判断必考 | ⭐⭐⭐⭐⭐ |
| ② | 二维及多维数组 | C++多维数组的定义与使用——编程题基础 | ⭐⭐⭐⭐ |
| ③ | 结构体 | 结构体定义、数组、指针、嵌套、传参、const——数据组织核心 | ⭐⭐⭐⭐ |
| ④ | 函数进阶 | 定义/调用/声明、形参实参、作用域、值传递与引用传递 | ⭐⭐⭐⭐⭐ |
| ⑤ | 递推算法 | 递推思想与递推关系式推导——编程题高频题型 | ⭐⭐⭐⭐⭐ |
| ⑥ | 排序算法 | 冒泡/插入/选择排序、复杂度与稳定性——选择+编程双热点 | ⭐⭐⭐⭐⭐ |
| ⑦ | 文件操作 | 文件重定向、读写操作——编程题可能涉及 | ⭐⭐⭐ |
| ⑧ | 异常处理 | 异常处理机制与常用方法——概念题为主 | ⭐⭐⭐ |
📌 考频星级说明:基于历年 GESP 四级真题统计分析,⭐越多表示出题频率越高、分值占比越大。
三、🔍 八大考点逐一拆解
下面逐个说明每个考点 "考什么" 和 "怎么考"。
① 指针
🎯 考什么
- 指针类型:理解指针是存储内存地址的变量,掌握
类型*的声明方式 - 指针变量的定义与赋值:取地址运算符
&、指针变量的初始化与赋值 - 解引用:通过指针访问所指向的内容,掌握
*运算符的使用 - 指针与数组的关系、指针的基本运算(加减、比较)
📝 怎么考
| 题型 | 出题方式 |
|---|---|
| 单选题 | 指针变量的地址与值分析、指针运算结果判断、&与*的组合使用。指针的加减运算与地址偏移计算 |
| 判断题 | "指针变量存储的是地址"(对)、"所有指针大小相同"(对,与类型无关,但是在GESP/C++常见环境中,指针变量的大小通常与类型无关,均由系统位数决定) |
| 编程题 | 利用指针交换变量、指针遍历数组、结构体指针的使用 |
cpp
#include <iostream>
using namespace std;
int main() {
int a = 10;
int* p = &a; // p存储a的地址
cout << *p << endl; // 解引用,输出10
*p = 20; // 通过指针修改a的值
cout << a << endl; // 输出20
return 0;
}🔥 高频考点:指针是四级的标志性新增内容,几乎每次考试都有2~4道选择/判断题考查
&(取地址)和*(解引用)的组合运算。务必区分"指针的值"(地址)和"指针指向的值"(数据)。
⚠️ 易错点:未初始化的指针(野指针)不能解引用!
int* p; *p = 10;是未定义行为,考试常考判断题。
② 二维及多维数组
🎯 考什么
- C++二维数组:定义、初始化、按行/按列遍历、下标访问
- 多维数组:三维及以上数组的定义与使用(考纲要求了解)
- 二维数组在内存中的存储方式(行优先存储)
- 二维数组作为函数参数:传递规则及第二维长度必须显式指定的要求
📝 怎么考
| 题型 | 出题方式 |
|---|---|
| 单选题 | 二维数组下标计算、数组元素遍历结果、初始化列表分析 |
| 判断题 | "二维数组在内存中按行优先存储"(对)、行列下标的起始值 |
| 编程题 | 矩阵处理(转置、求和、对角线等)、二维数组存储表格数据 |
cpp
#include <iostream>
using namespace std;
int main() {
// 定义并初始化3行4列的二维数组
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 按行遍历
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
cout << matrix[i][j] << " ";
}
cout << endl;
}
return 0;
}🔥 高频考点:二维数组的行优先存储和下标计算是选择题常客。例如
a[2][3]在内存中紧跟在a[2][2]之后。编程题中矩阵转置、杨辉三角等经典题型也要熟练。
③ 结构体
🎯 考什么
- 结构体定义和使用:
struct关键字、成员变量、成员访问(.运算符) - 结构体数组:多个同类型结构体的批量管理
- 结构体指针:
->运算符、通过指针访问结构体成员 - 结构体嵌套结构体:一个结构体的成员是另一个结构体类型
- 结构体做函数参数:值传递 vs 引用传递/指针传递
- 结构体中 const 使用场景:const 修饰结构体引用/指针,防止误修改
📝 怎么考
| 题型 | 出题方式 |
|---|---|
| 单选题 | 结构体成员访问(. vs ->)、结构体大小计算、嵌套成员访问 |
| 判断题 | "结构体可以嵌套自身类型的指针"(对)、const修饰结构体的效果 |
| 编程题 | 用结构体存储学生信息并排序、结构体数组的查找与统计 |
cpp
#include <iostream>
#include <string>
using namespace std;
// 定义日期结构体
struct Date {
int year, month, day;
};
// 定义学生结构体(嵌套Date)
struct Student {
string name;
int score;
Date birthday; // 结构体嵌套
};
// const引用传递:避免拷贝,同时防止修改
void printStudent(const Student& s) {
cout << s.name << " " << s.score << " "
<< s.birthday.year << "-" << s.birthday.month
<< "-" << s.birthday.day << endl;
}
int main() {
Student stu = {"Alice", 95, {2010, 5, 20} };
printStudent(stu);
// 结构体指针
Student* p = &stu;
p->score = 100; // 通过指针修改成员
cout << p->score << endl; // 输出100
return 0;
}🔥 高频考点:结构体是四级编程题的数据组织核心。
.(对象访问)与->(指针访问)的区别几乎每次必考。结构体做函数参数时,值传递会拷贝整个结构体,大结构体应使用引用或指针传递。
⚠️ 易错点:结构体可以包含自身类型的指针(如链表节点
Node* next),但不能包含自身类型的非指针成员(会导致无限递归大小)。
④ 函数进阶
🎯 考什么
- 函数的定义、调用与声明:函数原型、定义位置与声明的关系
- 形参与实参:形式参数与实际参数的对应关系
- 全局作用域与局部作用域:变量的可见性与生命周期、作用域嵌套
- 值传递与引用传递:
&修饰形参实现引用传递、函数内修改实参
📌 考纲明确不包含匿名函数(Lambda)和类的括号运算符重载。
📝 怎么考
| 题型 | 出题方式 |
|---|---|
| 单选题 | 函数声明与定义的区别、作用域判断(同名变量遮蔽)、值传递vs引用传递结果 |
| 判断题 | "函数必须在调用前声明"(对)、"局部变量在函数返回后仍可访问"(错) |
| 编程题 | 编写函数实现特定功能、引用传递交换变量、利用全局/局部变量解题 |
cpp
#include <iostream>
using namespace std;
// 函数声明(原型)
void myswap(int& a, int& b); // 引用传递
int add(int a, int b); // 值传递
int g_x = 100; // 全局变量
int main() {
int x = 3, y = 7;
myswap(x, y); // 调用后x=7, y=3
cout << x << " " << y << endl;
cout << add(10, 20) << endl; // 输出30
return 0;
}
// 函数定义
void myswap(int& a, int& b) { // 引用传递:形参是实参的别名
int temp = a;
a = b;
b = temp;
}
int add(int a, int b) { // 值传递:形参是实参的副本
return a + b;
}🔥 高频考点:值传递与引用传递的区别是每次必考的核心知识点。记住口诀:值传递改副本,引用传递改原件。引用传递的标志是形参类型中的
&。
⚠️ 易错点:函数如果在调用之后定义,必须在调用之前写函数声明(原型)。否则编译器找不到函数会报错。这是程序阅读题的常见陷阱。
⑤ 递推算法
🎯 考什么
- 递推算法基本思想:从已知初始条件出发,利用递推关系式逐步推算出最终结果
- 递推关系式推导:根据问题描述找出状态之间的关系,建立递推公式
- 经典递推模型:斐波那契数列、爬楼梯问题、过河卒、平面分割等
- 递推与递归的区别与联系
📝 怎么考
| 题型 | 出题方式 |
|---|---|
| 单选题 | 根据递推式计算前若干项、递推代码填空、初值与边界判断 |
| 判断题 | "递推必须使用递归实现"(错,可用循环)、递推关系式的正确性 |
| 编程题 | 根据题意推导递推式并编程求解(经典题型:斐波那契变形、路径计数) |
cpp
#include <iostream>
using namespace std;
// 斐波那契数列:f(n) = f(n-1) + f(n-2)
// 递推实现(非递归,效率高)
long long fib(int n) {
if (n <= 1) return n;
long long a = 0, b = 1; // 初始条件
for (int i = 2; i <= n; i++) {
long long c = a + b; // 递推关系式
a = b;
b = c;
}
return b;
}
int main() {
cout << fib(10) << endl; // 输出55
return 0;
}🔥 高频考点:递推是四级编程题的热门题型。解题关键在于:①确定状态含义;②找出递推关系式;③明确初始条件和边界。选择题常考递推代码的填空和结果推算。
⑥ 排序算法
🎯 考什么
- 冒泡排序(Bubble Sort):相邻比较交换、每轮确定一个最大值
- 插入排序(Insertion Sort):将元素插入已排序序列的合适位置
- 选择排序(Selection Sort):每轮选出最小(大)值放到已排序末尾
- 时间复杂度与空间复杂度:三种排序的复杂度分析
- 算法稳定性:相等元素排序后相对位置是否保持
- 简单算法复杂度估算:多项式复杂度(O(n), O(n²), O(n³))与指数复杂度(O(2ⁿ))
📝 怎么考
| 题型 | 出题方式 |
|---|---|
| 单选题 | 排序过程模拟(第几轮后的序列)、复杂度比较、稳定性判断 |
| 判断题 | "冒泡排序是稳定的"(对)、"选择排序是稳定的"(错) |
| 编程题 | 实现某种排序、排序后进行统计/查找 |
| 排序算法 | 最好时间 | 平均时间 | 最坏时间 | 空间复杂度 | 稳定性 |
|---|---|---|---|---|---|
| 冒泡排序 | O(n) | O(n²) | O(n²) | O(1) | ✅ 稳定 |
| 插入排序 | O(n) | O(n²) | O(n²) | O(1) | ✅ 稳定 |
| 选择排序 | O(n²) | O(n²) | O(n²) | O(1) | ❌ 不稳定 |
🔥 高频考点:排序算法是四级的绝对重点,选择题和编程题都会涉及。必须能手写三种排序的代码,并记住各自的复杂度和稳定性。特别注意选择排序不稳定这一常考判断题。
⚠️ 易错点:冒泡排序在已有序时最好时间复杂度为 O(n)(需加标志位优化),但选择排序无论如何都是 O(n²)。考试中常比较这三种排序在不同数据情况下的效率。
⑦ 文件操作
🎯 考什么
- 文件重定向:
freopen函数的使用,将标准输入/输出重定向到文件 - 读操作:从文件读取数据(
ifstream/fscanf/freopen+cin) - 写操作:向文件写入数据(
ofstream/fprintf/freopen+cout) - 读写操作:同时读写文件、文件指针的定位与关闭
📝 怎么考
| 题型 | 出题方式 |
|---|---|
| 单选题 | freopen参数含义、文件打开模式(r/w/a)判断、文件操作代码阅读 |
| 判断题 | "文件使用后必须关闭"(对)、文件重定向后能否恢复标准输入 |
| 编程题 | 从文件读入数据处理后输出到文件(配合算法综合考查) |
cpp
#include <iostream>
#include <fstream>
using namespace std;
int main() {
// 方式一:freopen重定向(竞赛常用)
// freopen("input.txt", "r", stdin);
// freopen("output.txt", "w", stdout);
// 方式二:fstream文件流
ofstream fout("output.txt");
fout << "Hello, GESP!" << endl;
fout.close();
ifstream fin("output.txt");
string s;
fin >> s;
cout << s << endl; // 输出 Hello,(>> 以空格为分隔符)
fin.close();
return 0;
}🔥 高频考点:
freopen是竞赛中最常用的文件操作方式,需记住三个参数:文件名、打开模式("r"读/"w"写/"a"追加)、文件流(stdin/stdout)。考试中可能出现编程题要求文件读写。
⑧ 异常处理
🎯 考什么
- 异常处理基本机制:
try、throw、catch三件套 - 异常的抛出与捕获:在代码中检测错误并抛出异常,在上层捕获处理
- 常用方法:标准异常类(
exception)、自定义异常、多重 catch 块 - 异常处理的执行流程:正常执行与异常发生时的跳转逻辑
📝 怎么考
| 题型 | 出题方式 |
|---|---|
| 单选题 | try-catch执行流程分析、异常抛出后代码执行顺序、catch匹配规则 |
| 判断题 | "try块中没有异常时catch不执行"(对)、"一个try只能对应一个catch"(错) |
| 编程题 | 一般不单独出异常编程题,可能在代码阅读中考查 |
cpp
#include <iostream>
using namespace std;
int divide(int a, int b) {
if (b == 0) {
throw "Division by zero!"; // 抛出异常
}
return a / b;
}
int main() {
try {
cout << divide(10, 2) << endl; // 正常输出5
cout << divide(10, 0) << endl; // 抛出异常
cout << "This won't print" << endl; // 不会执行
} catch (const char* msg) {
cout << "Error: " << msg << endl; // 捕获并处理
}
cout << "Program continues" << endl; // 正常执行
return 0;
}🔥 高频考点:异常处理在四级中以概念题为主,通常1~2道选择/判断。重点掌握 try-throw-catch 的执行流程:try 中抛出异常后,后续语句跳过不执行,直接进入匹配的 catch 块。
四、📈 备考建议:如何高效攻克四级?
1️⃣ 抓住核心,重点突破
八大考点并非同等重要。根据历年真题分析,指针、结构体、函数进阶、递推算法、排序算法这五大模块占据了约 80% 的分值,尤其是编程题几乎必从这些模块出题。优先攻克这些重点,再兼顾文件操作和异常处理。
2️⃣ 选择/判断靠概念,编程题靠手感
- 选择 + 判断(50分):吃透概念 + 刷真题,重点是代码阅读填空和语法细节辨析
- 编程题(50分):必须动手写!递推模板 + 三种排序模板 + 结构体使用是必备武器
3️⃣ 指针与结构体是四级的"分水岭"
相比三级,四级最大的跨越就是指针和结构体。很多同学在这里感到困难,但它们是后续所有数据结构(链表、树、图)的基础。建议:
- 指针:多画图理解内存模型,分清地址和值
- 结构体:多写代码,熟练掌握
.和->的使用场景
4️⃣ 真题是最好的复习资料
GESP四级历年真题是最好的备考材料。建议按以下顺序刷题:
- 先按模块分类刷 → 找到自己的薄弱点
- 再按套卷限时练 → 训练时间分配能力
- 最后复盘错题 → 查漏补缺
五、📝 实战练习
练习题 1(判断题)
题目: 指针变量存储的是它所指向变量的值,而不是地址。( )
🔑 点击查看答案与解析
答案: 错误 ❌
解析: 指针变量的本质是存储内存地址的变量,而不是存储值。就像写着门牌号的纸条,纸条上写的是"地址"而不是"房子里的东西"。要获取指针指向的值,需要用 * 解引用运算符。
练习题 2(单选题)
题目: 有定义 int a[3][4];,以下说法正确的是( ) A. 数组 a 在内存中按列优先存储
B. a[1][2] 的地址紧跟在 a[1][1] 之后
C. 二维数组做函数参数时,第一维长度可以省略,第二维长度必须显式指定
D. a[2][4] 是合法的数组元素访问
🔑 点击查看答案与解析
答案: C
解析:
- A 错误:C++ 二维数组按行优先存储,不是列优先
- B 正确但不完整:
a[1][2]确实紧跟a[1][1],但 C 更全面准确 - C 正确 ✅:二维数组作为函数参数,第一维可以省略(由调用者决定行数),但第二维长度必须指定,编译器需要知道每行有多少元素才能计算地址
- D 错误:下标从 0 开始,第二维最大下标是 3(因为长度是 4),
a[2][4]越界
💡 记忆口诀:行优先,像读书——从左到右,从上到下。
练习题 3(单选题)
题目: 有以下结构体定义,说法正确的是( )
cpp
struct Node {
int data;
Node* next;
};A. 该定义有语法错误,结构体不能包含自身类型的成员
B. next 是 Node 类型的成员变量
C. 通过结构体指针访问成员用 . 运算符
D. 这是链表节点的典型定义方式
🔑 点击查看答案与解析
答案: D
解析:
- A 错误:结构体不能包含自身类型的非指针成员(会导致无限递归大小),但可以包含自身类型的指针(指针大小固定)
- B 错误:
next是Node*类型(指针),不是Node类型 - C 错误:通过指针访问结构体成员用
->运算符,用对象访问才用. - D 正确 ✅:这就是链表节点的标准定义——数据域 + 指针域
练习题 4(程序阅读题)
题目: 阅读以下程序,输出结果是( )
cpp
#include <iostream>
using namespace std;
void func(int a, int& b) {
a++;
b++;
}
int main() {
int x = 5, y = 10;
func(x, y);
cout << x << " " << y << endl;
return 0;
}A. 5 10
B. 6 10
C. 5 11
D. 6 11
🔑 点击查看答案与解析
答案: C
解析:
本题考查值传递 vs 引用传递:
a是值传递:形参a是实参x的副本,函数内修改a不影响x,所以x仍为 5b是引用传递:形参b是实参y的别名,函数内修改b就是修改y,所以y变为 11
💡 口诀:值传递改副本,引用传递改原件。引用传递的标志是形参类型后面有
&。
练习题 5(填空题)
题目: 斐波那契数列定义:f(0)=0, f(1)=1, f(n)=f(n-1)+f(n-2),则 f(7) = ______。
🔑 点击查看答案与解析
答案: 13
解析:
逐项递推计算:
- f(0) = 0
- f(1) = 1
- f(2) = f(1) + f(0) = 1 + 0 = 1
- f(3) = f(2) + f(1) = 1 + 1 = 2
- f(4) = f(3) + f(2) = 2 + 1 = 3
- f(5) = f(4) + f(3) = 3 + 2 = 5
- f(6) = f(5) + f(4) = 5 + 3 = 8
- f(7) = f(6) + f(5) = 8 + 5 = 13
递推的核心就是:从已知出发,一步步推出未知。就像爬楼梯,每一步都踩在前一步的基础上。
练习题 6(单选题)
题目: 对数组 [5, 2, 8, 2, 1] 进行升序排序,以下说法正确的是( ) A. 冒泡排序和选择排序都是稳定排序
B. 插入排序第一轮结束后(i=1处理完),数组变为 [5, 2, 8, 2, 1]
C. 选择排序第2轮结束后,数组前2个元素一定是最小的两个数
D. 冒泡排序最好情况时间复杂度是 O(n²)
🔑 点击查看答案与解析
答案: C
解析:
- A 错误:冒泡排序稳定,但选择排序不稳定(跨位置交换可能打乱相等元素的相对顺序)
- B 错误:插入排序 i=1 处理第2个元素(值为2),插入到已排序序列
[5]的正确位置,结果应为[2, 5, 8, 2, 1],而不是[5, 2, 8, 2, 1](那是原数组) - C 正确 ✅:选择排序每轮确定一个最小值放到已排序末尾,第 2 轮结束后,前 2 个位置就是整个数组中最小的两个数
- D 错误:冒泡排序最好情况(数组已有序,加标志位优化)时间复杂度是 O(n),不是 O(n²)
💡 记忆技巧:选择排序像选秀——每轮选出一个冠军,排在最前面。几轮结束,前几名就是全组最强的。
练习题 7(判断题)
题目: 使用 freopen("data.txt", "r", stdin); 后,可以用 cin 从 data.txt 文件中读取数据。( )
🔑 点击查看答案与解析
答案: 正确 ✅
解析: freopen 的作用是重定向标准输入输出流。freopen("data.txt", "r", stdin) 把标准输入 stdin 重定向到文件 data.txt,之后所有从 stdin 读取的操作(包括 cin 和 scanf)都会改为从文件读取。
🍎 生活比喻:就像把自来水管从"市政供水"接到"自家水箱",打开水龙头后流出来的就是水箱里的水了。
练习题 8(程序阅读题)
题目: 阅读以下程序,输出结果是( )
cpp
#include <iostream>
using namespace std;
int main() {
try {
cout << "A";
throw 100;
cout << "B";
} catch (int e) {
cout << "C" << e;
}
cout << "D";
return 0;
}A. ABCD
B. AC100D
C. AB100CD
D. AC100
🔑 点击查看答案与解析
答案: B
解析:
执行流程分析:
- 进入 try 块,输出
"A" - 执行
throw 100,抛出 int 类型异常 - 抛出异常后,try 块中后续语句全部跳过,所以
"B"不会输出 - 匹配到
catch (int e),进入 catch 块,输出"C"和100 - catch 块执行完毕后,程序继续正常执行,输出
"D"
最终输出:AC100D,选 B。
💡 关键结论:throw 之后、catch 之前的代码都不会执行。异常就像紧急刹车,后面的路直接跳过,跳到安全区域(catch)。
练习题 9(程序阅读题)
题目: 阅读以下程序,输出结果是( )
cpp
#include <iostream>
using namespace std;
int main() {
int a[5] = {10, 20, 30, 40, 50};
int* p = a;
cout << *p << " ";
p++;
cout << *p << " ";
p += 2;
cout << *p << endl;
return 0;
}A. 10 20 30
B. 10 20 40
C. 10 30 50
D. 20 30 50
🔑 点击查看答案与解析
答案: B
解析:
本题考查指针与数组的关系以及指针算术运算:
int* p = a;:数组名a是数组首元素的地址,p指向a[0](值为10)*p→ 10(输出第一个数)p++:指针向后移动 1 个int的位置(4字节),指向a[1](值为20)*p→ 20(输出第二个数)p += 2:指针再向后移动 2 个int的位置,指向a[3](值为40)*p→ 40(输出第三个数)
最终输出:10 20 40,选 B。
💡 指针加减的本质:
p + n移动n × sizeof(类型)字节,不是移动 n 个字节。int 指针加1,地址实际加4(32位系统)或8(64位系统)。
练习题 10(编程题)
题目: 题目描述:有 n 个学生,每个学生有姓名(string)和成绩(int)。请按成绩从高到低排序,如果成绩相同则按姓名的字典序从小到大排序。输出排序后的学生名单。
输入:第一行一个整数 n(n ≤ 100),接下来 n 行每行一个姓名和一个成绩
输出:排序后的 n 行,每行一个姓名和一个成绩
样例输入:
4
Alice 95
Bob 85
Charlie 95
David 90样例输出:
Alice 95
Charlie 95
David 90
Bob 85🔑 点击查看答案与解析
参考答案:
cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
struct Student {
string name;
int score;
};
// 比较函数:按成绩降序,成绩相同则按姓名字典序升序
bool compare(const Student& a, const Student& b) {
if (a.score != b.score)
return a.score > b.score; // 成绩高的在前
else
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].score;
}
sort(stu, stu + n, compare);
for (int i = 0; i < n; i++) {
cout << stu[i].name << " " << stu[i].score << endl;
}
return 0;
}解析:
本题考查结构体 + 排序算法的综合应用,是四级编程题的经典题型:
- 结构体:用
Student结构体封装姓名和成绩 - 自定义比较函数:
sort函数的第三个参数决定排序规则- 主排序键:成绩降序(
a.score > b.score) - 次排序键:姓名字典序升序(
a.name < b.name)
- 主排序键:成绩降序(
- 排序:使用
algorithm库中的sort函数(也可以手写冒泡/插入/选择排序)
样例分析:
- Alice 和 Charlie 都是 95 分,按姓名字典序,Alice < Charlie,所以 Alice 在前
- David 90 分排第三
- Bob 85 分排最后
🔥 四级高频编程题:结构体排序几乎每次考试都有,务必熟练掌握自定义比较函数的写法。
⭐ 必背速记卡
┌─────────────────────────────────────────────┐
│ GESP 四级 · 核心速记卡 │
├─────────────────────────────────────────────┤
│ 1. 指针存地址,解引用用 *,取地址用 & │
│ 2. p++ 移动 sizeof(类型) 字节,不是1字节 │
│ 3. 二维数组行优先存储,像读书从左到右 │
│ 4. 结构体 . 访问成员,结构体指针 -> 访问 │
│ 5. 值传递改副本,引用传递改原件(&) │
│ 6. 函数调用前必须声明(或定义在调用前) │
│ 7. 递推三要素:状态、递推式、初始条件 │
│ 8. 冒泡/插入稳定,选择排序不稳定 │
│ 9. 三种排序平均/最坏都是 O(n²) │
│ 10. 冒泡和插入最好情况 O(n)(已有序) │
│ 11. 选择排序始终 O(n²),无法提前终止 │
│ 12. freopen三参数:文件名、模式、流 │
│ 13. 文件用完要关闭,防止数据丢失 │
│ 14. try中抛出异常后,后续语句直接跳过 │
│ 15. catch 按类型匹配,从上到下依次检查 │
└─────────────────────────────────────────────┘