
1. 为什么 5 级是 GESP 的分水岭——先看清考试定位1.1 5级到底考什么很多刚开始准备 CCF GESP 的同学一上来就问“5级难不难”。我的答案是它比 3 级、4 级难出一个明显的台阶但还不至于像 7 级、8 级那样需要系统学完算法竞赛入门。说白了5 级是一个“从会写代码到会想算法”的过渡级。从考纲看5 级覆盖的内容大致包括一维、二维数组的深入使用以及数组与指针的关系字符串处理包括常见的查找、替换、排序操作结构体与自定义数据类型函数与递归尤其是递归的执行过程分析简单排序算法包括冒泡、选择、插入、桶排序基础算法思想如枚举、模拟、贪心初探位运算与二进制的基本应用。可以看到基础语法已经把“循环、分支、数组、字符串”都考完了5 级开始往“数据结构雏形 算法思维”上靠。这也是为什么很多报 5 级的同学明明平时作业都会做一上考场却觉得题“变了个样”。我带的不少学生3 级、4 级都是裸考一把过到 5 级就突然卡住了。原因很简单前面等级考的更多是“语法熟不熟”而 5 级开始考“思路有没有”。这个转变如果不提前适应考场上是很难靠临场发挥扛过去的。1.2 题型结构与时间分配GESP C 5 级考试的题型基本稳定跟 3、4 级类似由两部分组成客观题单选题 判断题主要覆盖语法细节、程序阅读、算法概念。编程题通常是 4 道程序题需要自己写代码并提交。客观题看着简单实际上很多都是“给一段程序让你求输出结果”或者“给你一个算法描述让你选复杂度”。这类题非常考验阅读代码的能力而不是死记硬背。编程题则是拉开差距的地方。4 道题一般呈阶梯分布第 1 道偏简单基本是纯模拟或者简单数组处理第 2 道开始上一点算法比如字符串处理或者枚举优化第 3 道和第 4 道就明显有区分度了可能会用到递归、多种排序的组合应用甚至需要一点贪心思维。时间上我建议客观题控制在 40 分钟以内剩下的时间全部留给编程题。你别小看这个分配很多同学在客观题上纠结太久导致后面编程题连题都读不完。要知道编程题一道就是几十分丢掉任何一道都可能让你和合格线擦肩而过。1.3 难度曲线与区分度GESP 的认证结果不是按“排名”给等级的而是看卷面分是否达到对应标准。5 级的合格线通常比 4 级高一个档而且题目设计上明显增加了“阅读量”和“陷阱量”。我用一个直观的对比说明考察维度4 级5 级语法考察以结构体、函数定义为主增加指针与数组的互操作算法要求会写冒泡排序即可要求能分析排序的稳定性、复杂度读程序难度程序长度 10 行左右程序长度可到 20 行以上含递归编程题量偏模板化需要自主设计状态与循环逻辑所以5 级的“区分度”主要体现在两个地方第一你能不能在不借助调试工具的情况下读懂一段含递归或指针的程序第二你能不能把一个实际问题抽象成数组、循环和函数并实现出来。这两个能力都靠平时积累临时抱佛脚效果很差。2. 核心考点逐一拆解从数组到指针真题最爱考这些2.1 多维数组与指针大多数人的第一道坎先说数组。5 级对一维数组的要求不再是“会用下标存取”而是要求你理解数组在内存里是连续存储的以及“数组名可以作为指针使用”这个特性。比如下面这类程序阅读题#include iostream using namespace std; int main() { int a[5] {10, 20, 30, 40, 50}; int *p a; cout *(p 3) endl; cout a[3] endl; cout p[1] endl; return 0; }输出是什么答案是 40、40、20。这个例子看起来简单但它同时考了三个知识点*(p 3)等价于p[3]p a之后p和a都指向数组首元素下标运算本质上就是指针算术运算。出错的同学多半是搞不清*(p 3)到底是取地址还是取值。你就记住一条数组名和指针在“访问元素”这件事上是等价的a[i]和*(a i)是完全相同的操作。这个想通了二维数组的很多问题也会迎刃而解。二维数组的核心难点在于a[i][j]到底是怎么定位到那个元素的计算公式如下地址 首地址 i * 每行元素个数 * sizeof(元素类型) j * sizeof(元素类型)看一道经典风格的题目有定义int a[3][4]已知a的首地址是 1000每个int占 4 字节那么a[2][1]的地址是多少按公式算1000 2 * 4 * 4 1 * 4 1000 32 4 1036这里特别容易出错的地方是“每行 4 个元素”所以i要乘的是4 * 4 16字节不是乘 4。很多同学粗心把行偏移算成了2 * 4 8结果选错答案。这种题在客观题里几乎是必考的计算时宁可多写一步也不要心算。2.2 字符串处理编程题的高频素材字符串在 5 级里出现频率非常高。它不单独考语法而是跟数组、循环、条件判断组合起来变成一道完整的编程题。常用的 C 字符串处理方式有两种一是用char数组C 风格二是用string类。以 5 级考试的要求我更推荐你熟练使用string因为它的length()、substr()、find()等成员函数可以节省大量编码时间而且不容易越界。举个例子判断一个字符串是不是回文串#include iostream #include string using namespace std; int main() { string s; cin s; bool flag true; int left 0, right s.length() - 1; while (left right) { if (s[left] ! s[right]) { flag false; break; } left; right--; } cout (flag ? Yes : No) endl; return 0; }这题的思路核心是“双指针”一个从头往后走一个从尾往前走。它考察的并不止是语法而是你能否想到用两个下标去维护一个区间。这种思想在后面的排序、查找中也会反复出现所以值得多练。很多初学者会写一个比较笨的版本先把字符串反转再跟原串比较。这样写功能上没错但它多开了一份内存也多跑了一次循环。在 5 级阶段你要开始有意识地养成“关注时间和空间”的习惯这是从“会写代码”到“写好代码”的分界点。2.3 递归与函数调用必须拿下的基础分递归是 5 级的重点也是很多学生的痛点。考试里它会以各种形式出现读程序题让你写出递归结果编程题让你用递归实现某个功能。递归的关键就一句话函数自己调用自己但是每次调用的参数必须向着“更简单”的方向变化直到满足递归出口。出口写不对程序就死循环了。我见过最经典的一种错误是把“出口条件”和“递归调用”的顺序搞反。比如求斐波那契数列int fib(int n) { return fib(n - 1) fib(n - 2); // 少了出口 }这样写法在编译阶段不会报错但运行起来直接栈溢出。正确写法是int fib(int n) { if (n 1) return n; return fib(n - 1) fib(n - 2); }有了if (n 1) return n;这句递归才有“停止”的地方。口诀很土但很管用先写出口再写递归。在读程序题里递归一般会配合“全局变量”或者“静态变量”出题这时候你要特别注意变量是在哪一层被修改的。我的建议是在草稿纸上画“递归调用树”一层一层地往下展开不要凭感觉跳步。每画一层记下当前参数和返回值最后再回推。看起来慢实际上正确率极高。2.4 排序、枚举与模拟稳拿编程题大头5 级对排序的要求是不光会写还要能比较不同排序的耗时和稳定性。冒泡排序、选择排序、插入排序这三种最基础必须闭着眼都能写出来。桶排序也要会因为它在很多“值域有限”的题里效果拔群。冒泡排序的核心是相邻元素两两比较把较大的往后移动。它的时间复杂度是 O(n²)。选择排序则每次找最小值放到前缀位置。插入排序更适合“近乎有序”的数组。给你一个快速判断方法冒泡排序相邻交换稳定选择排序选最值交换不稳定插入排序逐个插入到前面有序序列稳定。“稳定性”这个考点客观题经常出现。你要是记不清就理解一下排序后相等元素的相对位置是否保持不变。能保持的是稳定排序不能保持的是不稳定排序。选择排序之所以不稳定是因为它可能把前面的相等元素换到后面去。编程题里排序往往不会单独考而是作为“中间步骤”。比如一道题让你按总分排序输出学生信息这时候你要做的就是先结构体存人再写排序函数。这类题只要结构体定义清楚、比较条件写对基本就是送分题一定不能丢。3. 真题风格精讲拿 2025 年 9 月场次常见考法举例3.1 示例题一二维数组下标计算这类题属于“客观题必考 编程题铺垫”的题型。下面我用一道接近真题风格的题目帮你找感觉重点不是题目本身而是解法思路。题目大意给出如下定义要求计算a[1][3]的值。#include iostream using namespace std; int main() { int a[2][4] { {1, 2, 3, 4}, {5, 6, 7, 8} }; int *p a[0][0]; cout *(p 1 * 4 3) endl; return 0; }答案是8。拆解一下a[1][3]对应数组第二行第四列因为 C 数组下标从 0 开始。如果用一个一维指针p指向整个二维数组的首地址那么要访问a[i][j]就相当于访问*(p i * 列数 j)。这里的列数是 4所以偏移量是1 * 4 3 7从首元素往后数 7 个元素正好是第二行的最后一个元素 8。做题时最容易算错的地方是“偏移量从 0 开始数”。很多人会把a[1][3]误认为偏移 8 或 9就是因为没有意识到首元素已经是第 0 个位置。我的习惯是在草稿上先写“每行 4 个元素”再写出完整公式然后代入计算。一来不容易错二来检查也方便。3.2 示例题二字符串回文判断刚才写过回文判断的代码这里我换一种考法不是让你写代码而是给一段程序让你判断它能不能正确判断回文。这种“阅读程序”的题在客观题里屡见不鲜。看这段代码#include iostream #include string using namespace std; int main() { string s; cin s; for (int i 0; i s.length(); i) { if (s[i] ! s[s.length() - 1 - i]) { cout No endl; return 0; } } cout Yes endl; return 0; }这段代码看似正确其实有个运行时隐患当i从 0 到s.length()-1遍历时会两次判断相同位置的对子。比如s abai0时比较s[0]和s[2]i2时又比较s[2]和s[0]。虽然一般不会影响结果但效率浪费了。更严重的问题是如果字符串长度是偶数比如ab程序仍然能正确输出No但如果换成只遍历一半就会更简洁高效。所以读程序的题你要关注的不仅是“能不能跑对”还要关注“有没有更好的写法”。5 级开始题目会故意给你一些“功能正确但实现很笨”的代码然后问你这代码有什么问题。掌握双指针写法既能解题也能在考试时给阅卷老师一个更好的印象。3.3 示例题三递归求方案数递归在编程题里最常见的应用之一就是“计数问题”。比如经典的爬楼梯问题每次可以迈 1 阶或 2 阶台阶问到第 n 阶有多少种走法。这就是一个递归问题状态转移方程f(n) f(n - 1) f(n - 2)边界条件f(1) 1, f(2) 2照着这个写代码#include iostream using namespace std; int climb(int n) { if (n 1) return 1; if (n 2) return 2; return climb(n - 1) climb(n - 2); } int main() { int n; cin n; cout climb(n) endl; return 0; }这段代码能跑但n稍微大一点就会超时因为很多子问题被重复计算了。5 级考试未必要求你优化到多深但如果你能在递归基础上加一个记忆化数组立刻就能拉开和别人差距int memo[1005]; int climb(int n) { if (n 1) return 1; if (n 2) return 2; if (memo[n] ! 0) return memo[n]; memo[n] climb(n - 1) climb(n - 2); return memo[n]; }记忆化的核心思想是“算过一次就存下来下次直接用”。这个思路其实不复杂但它体现了一种进阶的算法意识你开始考虑“如何避免重复劳动”。我在辅导时经常跟学生说5 级不要求你发明算法但要求你能比别人多想一步。3.4 示例题四结构体排序的提交细节结构体排序是 5 级编程题里的常客。它的核心点不在于排序算法本身而在于“比较规则”的写法。题目通常会这样描述每个学生有学号和总分要求按总分从高到低排序如果总分相同按学号从小到大排。用sort配合自定义比较函数是最省力的写法。你需要写一个比较函数#include iostream #include algorithm using namespace std; struct Student { int id; int score; }; bool cmp(Student a, Student b) { if (a.score ! b.score) return a.score b.score; return a.id b.id; } int main() { Student stu[105]; int n; cin n; for (int i 0; i n; i) { cin stu[i].id stu[i].score; } sort(stu, stu n, cmp); for (int i 0; i n; i) { cout stu[i].id stu[i].score endl; } return 0; }这里要注意几个细节比较函数cmp必须返回bool并且要严格遵守“严格弱序”要求。如果相等时返回truesort的行为是未定义的可能导致程序崩溃。结构体数组sort的排序范围是[stu, stu n)左闭右开别多写一个元素。如果题目要求先按总分降序、再按学号升序逻辑必须完整写进cmp不能只写一个条件。在考场上这种题最容易出的问题是思路清楚但写cmp时不小心把大于号写反了。我的建议是写完排序后立刻用一个三个元素的小数组自测一个正常数据、一个分数相同的数据、一个边界数据。花不了 30 秒但能避免 10 分以上的损失。4. 实战中的编译、调试与提交技巧4.1 快读与输入输出很多人不知道GESP 考试的判题环境对运行时间是有要求的。虽然 5 级题目大多数据量不大但如果你用cin/cout而不关同步遇到大数据时可能会明显变慢。我建议在写代码时统一加上这两行ios::sync_with_stdio(false); cin.tie(nullptr);这两行的作用分别是取消 C 标准流和 C 标准流之间的同步以及解除cin和cout的绑定。加上之后cin/cout的速度会接近scanf/printf。虽然不一定会用到但习惯养成了后面六级、七级考试都会受益。如果你不想用cin/cout直接用scanf/printf也行。只是注意用它们读string会比较麻烦char数组会更顺手。5 级阶段我建议你以cin/cout为主配合好同步关闭就够了。4.2 下标越界与内存问题数组下标越界是运行时报错或者答案错误的最常见原因。比如你声明int a[100]却访问a[100]在本地编译器里可能不报错但在线上判题环境中可能就会导致随机错误。我见过学生调了半天程序最后发现是循环条件写成了i n导致多访问了一次数组末尾。这种错误的隐蔽之处在于本地运行可能碰巧不崩但数据一变就出错。我的排查方法很简单写代码时凡是看到数组下标心里默念“下标范围是 0 到 n-1”。再保守一点数组声明可以稍微开大一点比如需要n 100时就写105。这个习惯虽然有点“糙”但能有效避免边界问题。另外5 级开始涉及递归递归过深可能导致栈溢出。比如n 10000的递归栈可能就爆了。如果你发现程序运行到某些数据时突然退出优先检查递归深度考虑改成循环或加记忆化。4.3 用 VSCode 调试递归不少同学在考场外准备时使用的编译器是 Dev-C 或者 Code::Blocks。这两种没什么问题但如果你想认真准备后面的六级、七级我更推荐趁早转到 VSCode。VSCode 配合 C/C 扩展调试体验比 Dev-C 好太多。简单配置流程是安装 VSCode 和 C/C 扩展安装 MinGW-w64 编译器并配置环境变量在.vscode文件夹里写好launch.json和tasks.json。配置好之后你就可以在递归函数里打上断点查看每一层调用时变量的值。这对理解递归执行过程帮助极大。很多学生看递归代码看得头大但自己亲手单步调试几次之后一下子就通了。调试不是“不会写代码才用”的工具而是帮助你理解程序运行过程的放大镜。5. 常见失分点排查考场上最容易丢分的地方5.1 选择题的常见陷阱GESP 客观题特别喜欢出的一个套路是给你一个说法让你判断对错。比如“冒泡排序是稳定的”这句话看起来很简单但换个说法“选择排序是稳定的”就很多人开始犹豫。还有一个高频陷阱是关于数组和指针的。题目可能会写“一维数组名是一个指针变量。”这句话是错的因为数组名虽然可以当作指针使用但它本质上是常量不能执行a这样的操作。很多学生一看到“数组名类似指针”就选了对结果就丢了分。应对客观题我的经验是别凭印象答题。遇到判断正误的题先在心里构造一个小例子。比如说到排序稳定性就举一个两元素相等的例子手动模拟一次排序过程看相对顺序变没变。虽然多花了十几秒但正确率会明显提升。5.2 编程题提交时的坑编程题最容易丢分的地方不是算法想不出而是输出格式不对。GESP 判题是严格按照输出内容比对的多一个空格、少一个换行都可能被判错。前几年我就见过一个学生算法思路完全正确但因为最后一行多输出了一个空格导致整道题 0 分。这种失误太可惜了。我的建议是读题时把“输出要求”那一句话圈出来写代码时统一用cout a b endl;这种模式避免手拼空格写完自测时重点检查输出是否跟样例一模一样包括行尾空格。另外题目如果没有特殊说明多个样例输出之间通常要换行。如果用了某种奇特的输出格式最好先用样例验证一遍。样例能过不代表所有数据都能过但样例都过不了基本必挂。5.3 时间不够怎么办编程题如果遇到一道想了 20 分钟还没思路果断先放一放做后面的题。5 级考试的编程题是按点给分的即使你只完成了部分功能可能也能拿到一部分分数。比如一道题要求排序你写了一个冒泡排序但因为复杂度高只能过 60% 的数据那也比空着强。更聪明的做法是“分段骗分”先写一个能过小数据的暴力版本拿到部分分剩下时间再去想优化。这在信息学竞赛里叫“拿稳暴力分”它可能不会让你拿满分但能保证你不至于翻车。我建议的做题顺序是先花 5 分钟把所有编程题都读一遍先把最有把握的题做完再做看似熟悉但有难度的题最后攻最难的题实在不行就写暴力版。这个顺序能保证你把“能拿的分”都拿到手不至于因为一道难题卡住导致后面简单题没时间写。6. 备考 5 级的路线图与资源清单6.1 60 天备考计划如果你离 2025 年 9 月认证还有大约两个月我推荐你按“三阶段走”第一阶段约 20 天回归基础扫清盲区。把一维数组、二维数组、字符串、结构体、函数、递归逐个过关。每学一个知识点就配套做题目。这个阶段不贪多但要求每个知识点都能独立写出代码。第二阶段约 20 天刷真题风格套题。这时可以直接拿历年 5 级真题练手重点不是分数而是分析每道题考了什么、自己卡在哪里。做完之后一定要整理错题本把所有“读程序题”里看不明白的代码重新手写一遍并运行。你亲手写过一遍的代码和你只是看过的代码记忆深度完全不同。第三阶段约 20 天模拟考试与查漏补缺。严格按考试时间做完整套题训练时间分配。同时把高频考点比如排序稳定性、二维数组地址计算、回文处理、递归边界再过一遍。这个阶段的目标是“稳”不再学新知识而是把已经会的知识练到不失误。6.2 刷题资源与编译环境配置刷题的地方我推荐还有在线评测平台比如洛谷、信奥赛题库之类搜索“GESP 真题”就能找到很多往年题目。考纲要求的知识点也可以按“入门到进阶”的顺序刷每天保证 3 到 5 道题持续两个月效果非常明显。环境方面我个人最推荐的组合是 VSCode MinGW-w64。在搜索引擎里搜“VSCode 配置 C 环境”能找到大量图文教程。配置过程其实不难但配置时容易踩坑的点在于launch.json里的program路径要和你的编译输出路径一致。很多学生配置完点调试按钮报错十有八九是路径没对齐。如果你的电脑配置比较旧或者不想折腾用 Dev-C 也完全够应付 GESP 5 级。关键是做题本身而不是纠结哪个编译器更好。我见过用 Dev-C 一路裸考到六级的学生也见过配好 VSCode 却一直拿来当编辑器用的学生。工具只是辅助能不能过级最终拼的还是代码量和思路熟练度。6.3 最后再分享一点个人经验带过这么多学生备考 GESP我最深的体会是5 级不是靠“背题”能过的但它也不是高不可攀。它考察的是你能否把零散的语法知识组合起来解决一个稍微完整一点的问题。这种组合能力没有捷径只能通过反复看题、写代码、调代码来提升。如果你现在离考试还有一段时间不要焦虑踏踏实实把本文里提到的每个考点都练一遍。尤其是数组与指针的关系、递归的出口写法、结构体排序的比较函数这三个点拿稳了5 级证书基本就离你不远了。