Skip to content

第1章 GESP 四级考点总览 ​


🎯 本章学习目标 ​

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

  1. 建立全局视野:清晰了解 GESP 四级考试的 8 大知识模块及其重要程度
  2. 掌握指针核心:理解指针的本质是内存地址,熟练使用 & 取地址和 * 解引用
  3. 玩转二维数组:掌握二维数组的定义、初始化、遍历和行优先存储规律
  4. 组织复杂数据:用结构体封装多种类型数据,掌握结构体数组、指针和嵌套
  5. 精通函数进阶:理解值传递与引用传递的区别,掌握函数声明与作用域规则
  6. 推导递推关系:从问题描述中找出递推关系式,用递推算法求解经典问题
  7. 手写三大排序:熟练编写冒泡、插入、选择排序代码,分析复杂度与稳定性
  8. 操作文件数据:使用 freopen 或文件流进行文件的读写操作
  9. 理解异常机制:掌握 try-throw-catch 的执行流程,能阅读异常处理代码
  10. 制定备考策略:根据各模块考频和自身情况,合理安排复习计划

🗺️ 本章知识图谱 ​

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. 再按套卷限时练 → 训练时间分配能力
  3. 最后复盘错题 → 查漏补缺

五、📝 实战练习 ​

练习题 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 仍为 5
  • b 是引用传递:形参 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

解析:

执行流程分析:

  1. 进入 try 块,输出 "A"
  2. 执行 throw 100,抛出 int 类型异常
  3. 抛出异常后,try 块中后续语句全部跳过,所以 "B" 不会输出
  4. 匹配到 catch (int e),进入 catch 块,输出 "C" 和 100
  5. 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;
}

解析:

本题考查结构体 + 排序算法的综合应用,是四级编程题的经典题型:

  1. 结构体:用 Student 结构体封装姓名和成绩
  2. 自定义比较函数:sort 函数的第三个参数决定排序规则
    • 主排序键:成绩降序(a.score > b.score)
    • 次排序键:姓名字典序升序(a.name < b.name)
  3. 排序:使用 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 按类型匹配,从上到下依次检查      │
└─────────────────────────────────────────────┘

百炼成钢,融会贯通