GESP 2026年3月C++四级真题完整解析
CCF GESP 编程能力等级考试 · 2026年3月认证
C++ 四级 · 满分100分 · 27题
获取 202603gesp4级完整真题及详细解析.pdf
请关注状元编程公众号,回复 202603gesp4
📋 考试概况
| 等级 | |
| 题量 | |
| 分值 | |
| 核心考点 |
一、单选题(每题2分,共30分)
第1题
执行下面程序后,输出为?
intf(int x = 2){ return x * 3; }intmain(){ cout << f() << " " << f(4); }A) 6 6 B) 6 12 C) 4 12 D) 2 12
答案:B(6 12)
f() 无实参,使用默认参数 x=2,返回 2×3=6;f(4) 实参为4,返回 4×3=12。
💡 知识点:默认参数。当函数声明中某参数有默认值时,调用时可省略该实参。默认参数必须从右向左依次设置。
第2题
执行下面代码后,输出为?
int a = 5;int* p = &a;int** q = &p;**q += 7;cout << a << " " << *p;A) 5 5 B) 12 12 C) 5 12 D) 12 5
答案:B(12 12)
q 是指向指针 p 的指针,p 指向 a。
**q→ 先解引用q得到p,再解引用p得到a**q += 7等价于a += 7,a从5变为12*p等价于a,也为12
💡 知识点:二级指针。
int** q表示"指向指针的指针",**q最终访问的是a本身。
第3题
已知二维数组 a[3][4] 和行指针 int (*p)[4] = a,表达式 *(*(p+2)+1) 的值为?
A) 7 B) 10 C) 11 D) 14
答案:B(10)
设数组初始值为按行存储的1~12:
a[0][0]=1 a[0][1]=2 a[0][2]=3 a[0][3]=4a[1][0]=5 a[1][1]=6 a[1][2]=7 a[1][3]=8a[2][0]=9 a[2][1]=10 a[2][2]=11 a[2][3]=12p+2→ 指向第3行(索引2)*(p+2)→ 第3行首地址,即a[2][0]的地址*(p+2)+1→a[2][1]的地址*(*(p+2)+1)→a[2][1] = 10
💡 知识点:数组指针与二维数组。
int (*p)[4]是"指向含4个int元素的一维数组的指针"。
第4题
值传递、引用传递、指针传递混合题,输出为?
voidfun(int a, int &b, int *c){ a+=1; b+=2; *c+=3; }intmain(){ int x=1,y=1,z=1; fun(x,y,&z); cout<<x<<" "<<y<<" "<<z; }A) 2 3 4 B) 1 3 4 C) 1 1 4 D) 1 3 1
答案:B(1 3 4)
a值传递:修改不影响x,x仍为1b引用传递:b+=2等价于y+=2,y=3c指针传递:*c+=3等价于z+=3,z=4
💡 知识点:三种参数传递方式的区别。值传递复制副本不影响原变量;引用传递直接操作原变量;指针传递通过地址间接操作原变量。
第5题
全局变量与局部变量、引用传递,输出为?
int x = 3;voidf(int& x){ x += 2; }intmain(){ int x = 10; f(x); cout << x << " " << ::x; }A) 12 3 B) 10 3 C) 12 5 D) 10 5
答案:A(12 3)
f(x)中的x是主函数局部变量(值10),引用传递后+2变为12::x为全局变量,值仍为3,未被修改
⚠️ 易错点:
f(int& x)的参数x绑定的是调用时传入的变量。在main中调用f(x)时,传入的是局部变量x(值10),不是全局变量x(值3)。::x是作用域解析运算符,专门访问全局变量。
第6题
下列结构体初始化写法,正确的是?
A)Point p = (1, 2);B)Point p = {1, 2};
C)Point p = new Point(1, 2);D)Point p = <1, 2>;
答案:B
structPoint { int x, y; };Point p = {1, 2}; // ✅ 正确A: (1,2)圆括号不能用于结构体初始化 ❌B:花括号 {}是正确的初始化方式 ✅C: new返回指针,不能赋给非指针变量 ❌D: <1,2>语法非法 ❌
第7题
结构体值传递与引用传递,输出为?
structS { int a; int b; };voidg(S s){ s.a += 10; } // 值传递voidh(S& s){ s.b += 10; } // 引用传递intmain(){ S s{1,2}; g(s); h(s); cout << s.a << " " << s.b; }A) 11 12 B) 1 12 C) 11 2 D) 1 2
答案:B(1 12)
g(s)值传递:函数内修改的是副本,不影响原结构体 →s.a仍为1h(s)引用传递:直接操作原结构体 →s.b从2变为12
第8题
关于递推算法的描述,正确的是?
A) 函数自己调用自己
B) 递推从已知初值出发,利用递推关系逐步推出后续结果
C) 递推算法的时间复杂度一定是指数级
D) 递推需要回溯过程
答案:B
A:函数自己调用自己是递归的特征 ❌ B:正确描述递推算法 ✅ C:递推不局限于指数复杂度 ❌ D:递推不需要回溯 ❌
💡 递推 vs 递归:递推是"自底向上"迭代推导;递归是"自顶向下"分解问题再合并。
第9题
执行 climb(6) 的返回值为?(类斐波那契数列)
A) 8 B) 13 C) 21 D) 34
答案:B(13)
数列前6项:1, 2, 3, 5, 8, 13
| 13 |
💡 斐波那契数列:
f(n) = f(n-1) + f(n-2),是递推算法最经典的例子。
第10题
判断排序算法稳定性:
初始: (90,'A'), (90,'B'), (80,'C'), (90,'D')排序后: (80,'C'), (90,'A'), (90,'B'), (90,'D')
A) 不稳定 B) 稳定 C) 无法判断 D) 取决于实现
答案:B(稳定)
相同分数90的三个元素排序后相对顺序未变(A→B→D),符合稳定排序定义。
💡 稳定排序:相同关键字元素在排序后保持原有相对顺序。常见的稳定排序有冒泡排序、插入排序、归并排序;不稳定的有选择排序、快速排序、堆排序。
第11题
插入排序,横线处应填写?
while(j >= 0 && __________){ a[j+1] = a[j]; j--; }A)a[j] < keyB)a[j] > keyC)a[j] >= keyD)a[j] != key
答案:B(a[j] > key)
升序插入排序,当前元素大于待插入元素时需后移,条件为 a[j] > key。
💡 注意:用
>而非>=可保证排序的稳定性。
第12题
下列代码段的时间复杂度为?(双重嵌套循环)
for (int i = 0; i < n; i++)for (int j = 0; j < n; j++)// O(1) 操作A) O(n) B) O(n log n) C) O(n²) D) O(2ⁿ)
答案:C(O(n²))
两层 for 循环各执行 n 次,总操作次数为 n×n = n²,时间复杂度为 O(n²)。
第13题
哪种方式不能将字符串输出重定向到文件 log.txt?
A)freopen("log.txt", "w", stdout); cout << "Welcome";B)std::ofstream outFile("log.txt"); cout << "Welcome"; outFile.close();C)std::ofstream outFile("log.txt"); outFile << "Welcome"; outFile.close();D) 使用Shell重定向 ./program > log.txt
答案:B
选项B创建了文件流 outFile,但使用 cout 输出,没有进行重定向,内容不会写入文件。
⚠️ 易错点:
freopen可以将stdout重定向到文件;ofstream需要直接用outFile << ...输出。创建ofstream对象不会自动改变cout的输出目标。
第14题
C++异常处理,输出结果是?
intdivi(int a,int b){ if(b==0) throw0; return a/b; }// catch(const char* msg) → 输出"A"// catch(int) → 输出"B"A) 输出"A" B) 输出"B" C) 程序崩溃 D) 无输出
答案:B(输出"B")
throw 0 抛出的是 int 类型异常,匹配第二个 catch(int) 块,输出 "B"。
💡 知识点:异常类型匹配。
catch块按声明顺序匹配,throw的类型决定哪个catch被执行。
第15题
排行榜元素向前移动,while循环条件应为?
A)i > 0 && cur.score > players[i-1].score
B)i > 0 && cur.score < players[i-1].score
C)i > 0 && cur.score >= players[i-1].score
D)i > 0 && cur.score <= players[i-1].score
答案:A
i > 0:防止越界cur.score > players[i-1].score:当前玩家分数高于前一位时继续向前移动(分数高排前面)
💡 这是插入排序的变体应用,在排行榜场景中按分数从高到低排序。
二、判断题(每题2分,共20分)
add10a = 5+10 = 15 | ||
&a[1][2] - &a[0][1] = 4 | ||
calc(5) | ||
f()main 输出全局变量10,合并为2010 | ||
nullptr(全局/静态变量才会) | ||
cout << "Hello" | ||
判断题精选解析
第3题:二维数组 a[3][4] 按行存储,&a[1][2] 是第1行第2列,&a[0][1] 是第0行第1列。两者地址差 = (1行 × 4列 + 2列) - (0行 × 4列 + 1列) = 6 - 1 = 5... 实际上:
a[0][1]的线性索引:0×4+1 = 1a[1][2]的线性索引:1×4+2 = 6差值 = 6 - 1 = 5?
但题目说答案是"不是5"——关键是 int 占4字节,地址差以元素个数为单位是5,但如果问的是字节数差则是 5×4=20。具体取决于题目表述。
⚠️ 仔细审题:地址运算的差值含义(元素个数 vs 字节数)是常见陷阱。
第4题:函数重载歧义。当存在 calc(int) 和 calc(double) 两个重载,调用 calc(5) 时 5 可以隐式转换为 int 或 double,编译器无法确定选择哪个,导致二义性错误。
第6题:局部变量(包括指针)存储在栈上,不会自动初始化,值是未定义的(垃圾值)。只有全局变量和静态变量才会自动初始化为0(指针为 nullptr)。
第9题:
插入排序:最好情况(已排序)O(n),最坏O(n²) 选择排序:无论什么情况都是O(n²) 两者复杂度不同,不能互相替代
第10题:
intf(int n){int result = 1;for (int i = 1; i <= n; i++) result *= i;return result;}这是用循环计算阶乘,循环执行n次,时间复杂度为 O(n),不是O(2ⁿ)。
三、编程题(每题25分,共50分)
3.1 山之谷
题目描述: N行M列网格,若某单元格海拔不高于其所有8个方向相邻单元格的海拔,则为"山谷",统计山谷数量。
数据范围: 1≤N,M≤100,1≤h≤10⁵
核心思路: 在网格外围填充一圈高海拔值(>10⁵),统一处理边界,遍历每个格子检查8个方向。
参考程序:
#include<iostream>usingnamespace std;intmain(){int n, m, cnt = 0, k;int h[105][105];// 8个方向偏移量int mark[8][2] = {{-1,-1},{-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}}; cin >> n >> m;for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) cin >> h[i][j];// 外围填充高海拔(哨兵法)for (int i = 0; i <= max(n,m)+1; i++) h[i][0] = h[0][i] = h[i][m+1] = h[n+1][i] = 1e6;// 遍历每个格子检查是否为山谷for (int i = 1; i <= n; i++)for (int j = 1; j <= m; j++) {for (k = 0; k < 8; k++)if (h[i][j] > h[i+mark[k][0]][j+mark[k][1]]) break;if (k == 8) cnt++; // 8个方向都不比当前低 → 山谷 } cout << cnt;return0;}💡 技巧:外围填充哨兵值(极大值),避免对边界格子单独判断,代码更简洁。
3.2 礼盒排序
题目描述: 对n个礼盒按以下规则升序排序:
总价格从小到大 总价相同 → 最贵商品价格从小到大 仍相同 → 最便宜商品价格从小到大 仍相同 → 编号从小到大
数据范围: 1≤n≤10³,1≤k≤10,商品价格≤10⁴
核心思路: 用结构体存储每个礼盒的编号、总价、最高价、最低价,使用 sort + 自定义比较函数排序。
参考程序:
#include<iostream>#include<algorithm>usingnamespace std;structbox {int bh, sum, max, min;} giftbox[1005];// 自定义比较函数:多关键字排序boolcmp(const box &a, const box &b){if (a.sum != b.sum) return a.sum < b.sum;if (a.max != b.max) return a.max < b.max;if (a.min != b.min) return a.min < b.min;return a.bh < b.bh;}intmain(){int n, k, b; cin >> n >> k;for (int i = 0; i < n; i++) { giftbox[i].bh = i + 1; // 编号从1开始 giftbox[i].sum = 0;for (int j = 1; j <= k; j++) { cin >> b; giftbox[i].sum += b;if (j == 1) { giftbox[i].max = b; giftbox[i].min = b;continue; }if (b > giftbox[i].max) giftbox[i].max = b;elseif (b < giftbox[i].min) giftbox[i].min = b; } }sort(giftbox, giftbox + n, cmp);for (int i = 0; i < n; i++)if (i == 0) cout << giftbox[i].bh;else cout << " " << giftbox[i].bh;return0;}💡 技巧:多关键字排序时,在
cmp函数中依次比较各个关键字,先比较的优先级更高。sort默认不稳定,但配合编号作为最后关键字可保证唯一排序结果。
📊 答案速查表
单选题
判断题
📈 考点分布分析
| 合计 | 100 | 100% |
💡 备考建议
扎实掌握三种参数传递方式:值传递、引用传递、指针传递是四级核心考点,务必理解每种方式对原变量的影响 理解指针与数组的关系:一级指针、二级指针、数组指针(行指针)的概念和用法,多做指针运算题 结构体是重点:结构体定义、初始化、值传递vs引用传递,是高频考点 排序算法要全面掌握:不仅会写代码,还要理解稳定性、时间复杂度、适用场景 递推与递归的区别:递推是迭代推导,递归是自调用分解,两者思想不同 异常处理的类型匹配: throw的类型决定匹配哪个catch块,注意类型转换规则编程题注意边界处理:使用哨兵法(外围填充特殊值)可以简化边界判断,减少代码复杂度 多关键字排序:掌握 sort+ 自定义cmp函数的写法,注意比较顺序对应优先级
📌 GESP C++ 四级 考察的核心是 C++ 中高级特性:指针、引用、结构体、异常处理,以及基础算法(排序、递推)。相比三级,四级更侧重于内存模型理解和算法思维,建议在掌握语法的基础上多练习编程实现。
夜雨聆风