
点击上方蓝字·关注我们




CCF编程能力等级认证,英文名Grade Examination of Software Programming(以下简称GESP),由中国计算机学会发起并主办,是为青少年计算机和编程学习者提供学业能力验证的平台。GESP覆盖中小学全学段,符合条件的青少年均可参加认证。GESP旨在提升青少年计算机和编程教育水平,推广和普及青少年计算机和编程教育。
GESP考察语言为图形化编程、Python编程及C++编程,主要考察学生掌握相关编程知识和操作能力,熟悉编程各项基础知识和理论框架,通过设定不同等级的考试目标,让学生具备编程从简单的程序到复杂程序设计的编程能力,为后期专业化编程学习打下良好基础。
本次为大家带来的是2024年9月份C++四级认证真题解析。
一、单选题(每题2分,共30分)
|
题号 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
|
答案 |
A |
B |
A |
B |
D |
A |
B |
D |
A |
B |
C |
A |
A |
A |
C |
1、在C++ 中,( )正确定义了一个返回整数值并接受两个整数参数的函数。
A. int add(int a, int b) { return a + b; }
B. void add(int a, int b) { return a + b; }
C. int add(a, b) { return a + b; }
D. void add(int a, int b) { return a - b; }
答案:A
解析:
A定义了一个返回类型为int 的函数add,它接受两个int 类型的参数,并正确地返回它们的和。
B定义了一个返回类型为void 的函数,不能返回任何值,所以不符合题意。
C缺少参数类型声明,a和b 必须指定为int 类型。
D同样定义了一个返回类型为void 的函数,不能返回值
2、在C++中,形参与实参的关系描述正确的是( )。
A.形参在函数调用时指定,实参在函数定义时传递
B.形参在函数定义时指定,实参在函数调用时传递
C.形参和实参可以互换
D.形参和实参必须是完全相同的类型,不能有任何差异。
答案:B
解析:
A错误,形参是在函数定义时指定,实参是在函数调用时传递的。
B正确,形参确实是在函数定义中声明的,而实参是在函数调用时提供的。
C错误,形参和实参不能互换,形参是函数定义中的占位符,实参是实际传递的值。
D错误,形参和实参不必完全相同类型,但必须兼容,例如可以使用类型转换。
3、运行以下代码,屏幕上将输出( )。

A. 100 200 100 200
B. 100 200 100 300
C. 100 200 200 200
D. 100 200 200 300
答案:A
解析:
main函数:首先输出全局变量var,其值为100。
调用function:局部变量var 被定义为200,所以cout << var << " "; 输出200。
cout << ::var << " "; 仍然输出全局变量var,其值未改变,输出100。
回到main 函数:此时全局var 仍然是100,输出200,所以最后输出的结果是100 200 100 200。
4、运行下面代码,屏幕上输出是( )。

A. 24
B. 9
C. 7
D.不确定
答案:B
解析:
int arr[3] = {24, 9, 7}; 定义了一个包含三个整数的数组。
int* p = arr; 将指针p 指向数组的第一个元素arr[0],即24。
p++;将指针p 移动到数组的下一个元素,即arr[1],此时p 指向9。
cout << *p << endl; 输出p 当前指向的值,即9
5、运行下面代码片段的结果是( )。
A.将x赋值为24
B.将y赋值为20
C.将q指向x的地址
D.将p指向y的地址
答案:D
解析:
int x = 20; 和int y = 24; 定义了两个整数变量。
int* p = &x; 将指针p 指向x 的地址。
int* q = &y; 将指针q 指向y 的地址。
p = q; 将指针p 更新为指向q 的地址,即p 现在指向y 的地址。
因此,最终的结果是p 指向了y 的地址,而没有改变x 或y 的值
6、在C++ 中,( )正确定义一个名为 student的结构体,其中包含一个name 字符数组和一个age 整数?
A. struct student { char name[20]; int age; };
B. student struct { char name[20]; int age; };
C. student struct { string name; int age; };
D. struct student { char[20] name; int age; };
答案:A
解析:
A正确地定义了一个名为student 的结构体,包含一个字符数组name 和一个整数age。
B错误,顺序不正确,struct应该在前。
C错误,使用了string,但缺少#include <string>头文件,且结构体定义不正确。
D错误,char[20] name; 语法不正确,应该是char name[20];
7、在C++ 中,( )正确声明了一个 3 行4 列的二维数组。
A. int arr[3, 4];
B. int arr[3][4];
C. int arr[4][3];
D. int arr(3, 4);
答案:B
解析:
A错误,int arr[3, 4]; 的语法不正确,逗号不应在数组声明中使用。
B正确,int arr[3][4]; 声明了一个3 行4 列的二维数组。
C虽然语法正确,但它声明的是一个4 行3 列的数组,而不是题目中要求的3 行4 列。
D错误,int arr(3, 4); 使用了圆括号,这是错误的语法,不能用于数组声明。
8、一个二维数组定义为int arr[3][4];(假设一个int变量占4个字节),则int arr[0] 占用( )个字节的内存。
A. 3
B. 4
C. 12
D. 16
答案:D
解析:对于定义为int arr[3][4]; 的二维数组,其中每个int 变量占用4 个字节,int arr[0] 指的是第一行的数组arr[0][0] 到arr[0][3]。
计算:每行有4 个int,每个占4 个字节,因此int arr[0] 占用的内存为4×4=16 字节
9、下面代码采用递推算法来实现整数 的阶乘( ),则横线上应填写( )。

A. result *= i;
B. result += i;
C. result *= result;
D. result += result;
答案:A
解析:
阶乘的定义是:n! = n * (n-1) * (n-2) * ... * 1。
在循环中,从2 到n,每次应将result 乘以当前的i,所以正确的填入应该是result *= i;。
10、在排序算法中,稳定性指的是( )。
A.排序后数据不会丢失
B.排序后相同元素的相对顺序保持不变
C.排序后数据不会被修改
D.排序后数据的时间复杂度不变
答案:B
解析:稳定排序算法在排序时,如果有两个相等的元素,排序后这两个元素的相对顺序保持不变。
其他选项的解释不符合稳定性的定义:
A错误,排序后的数据是否丢失与稳定性无关。
C错误,排序后数据可能会被修改(如位置改变)。
D错误,时间复杂度是排序算法的性能指标,与稳定性无关。
因此,正确答案是B
11、下面代码实现了冒泡排序函数,则横线上应填写( )。
A.
for (int j = 0; j < arr.size() - 1; j++)
B. for (int j = arr.size() - 1; j > 0; j--)
C. for (int j = 0; j < i; j++)
D. for (int j = i-1; j <=0; j--)
答案:C
解析:
冒泡排序的外层循环遍历数组,从后向前控制需要比较的元素范围。
内层循环for (int j = 0; j < i; j++) 则确保在当前遍历中比较到第i 个元素,保证了在每一轮中较大的元素会逐步“冒泡”到末尾
12、上一题算法的时间复杂度为( )。
O(n2)
O(2n)
O(1)
O(n)
答案:A
解析:冒泡排序的时间复杂度是O(n²)
13、 下面代码实现了插入排序函数(升序),则横线上应填写( )。

A. while (j >= 0 && nums[j] > base)
B. while (j > 0 && nums[j] > base)
C. while (j >= 0 && nums[j] < base)
D. while (j > 0 && nums[j] < base)
答案:A
解析:
插入排序的基本思想是将当前元素(base)插入到已排序的部分(nums[0]到nums[i-1])中。
在比较时,如果nums[j] 大于base,就需要将nums[j] 向后移动,以腾出位置来插入base。因此,条件应该是j >= 0(确保不越界)且nums[j] > base
14、小杨用文件重定向实现在log.txt 文件中输出日志,则下面横线上应填写( )。

A. cout << "This output will go to the log file." << endl;
B. log_file << "This output will go to the log file." << endl;
C. cout >> "This output will go to the log file." >> endl;
D. log_file >> "This output will go to the log file." >> endl;
答案:A
解析:由于之前的代码将cout 的输出重定向到了log_file,因此使用cout 输出的信息将被写入到log.txt 文件中。
选项A 正确,使用cout,而cout 已经被重定向到log_file。
其他选项不正确:
B错误,因为log_file 是一个文件流,应该通过cout 来输出,不能直接用log_file 来输出。
C错误,>>运算符是用于输入流的,不适用于输出。
D错误,类似于B,log_file不应直接使用>> 来输出
运行下面的代码,屏幕上将输出( )。

A. division by zero error result: caught an exception:
B. result: caught an exception: division by zero error
C. caught an exception: division by zero error
D. division by zero error caught an exception: division by zero error
答案:C
解析:
在main 函数中,y被设置为0。
调用divide(x, y) 时,由于y 为0,会抛出一个runtime_error 异常。
异常被catch 块捕获,输出"caught an exception: "后接异常的消息,使用e.what()获取错误信息。因此,最终输出是:caught an exception: division by zero error
二、判断题(每题2分,共20分)
|
题号 |
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
|
答案 |
√ |
√ |
× |
× |
√ |
× |
× |
× |
× |
√ |
1、 代码 int a = 10; int* p = &a; 可以正确定义指针和初始化指针。
答案:正确
解析:
int* p:这里定义了一个整型指针p。星号* 表示p 是一个指向整型的指针。
&a:&是取地址运算符,它返回变量a 的内存地址。
将&a 赋值给p,意味着p 现在指向a 的地址。
因此,指针p 正确地定义并初始化为指向变量a 的地址
2、在C++ 中,引用传递允许函数修改传递给它的参数的值。
答案:正确
解析:引用传递是C++中的一种参数传递方式,它允许函数直接操作传入的变量。通过使用引用(&),函数可以修改原变量的值,而不是只处理变量的副本。这种方式既高效又灵活
3、指针的大小与其所指向的变量的数据类型的大小相同。
答案:错误
解析:指针的大小通常与其所处的系统架构有关(如32 位或64 位),而与其所指向的变量的数据类型大小无关。在64 位系统中,所有指针通常都是8 字节,而在32 位系统中,通常是4 字节
4、二维数组的行的大小的必须在定义时确定,列的大小可以动态变化。
答案:错误
解析:在C++中,二维数组的行和列的大小都必须在定义时确定,不能动态变化。行的大小在定义时是固定的,列的大小可以在某些情况下通过动态分配实现,但如果使用标准数组,列的大小也必须是常量。
5、递推算法通过逐步求解当前状态和前一个或几个状态之间的关系来解决问题。
答案:正确
解析:递推算法通过利用已知的前一个或多个状态来逐步计算当前状态,从而解决问题。这种方法常用于动态规划和分治策略中,通过构建状态转移方程来有效地求解复杂问题。
6、选择排序是稳定的排序算法。
答案:错误
解析:选择排序是一种不稳定的排序算法。在选择排序过程中,相同元素的相对位置可能会改变,因此不能保证稳定性
7、插入排序的时间复杂度总是比冒泡排序低。
答案:错误
解析:不完全正确。插入排序在最佳情况下(已经排序的数组)时间复杂度为O(n),而冒泡排序在最佳情况下也为O(n)。但在平均和最坏情况下,插入排序的时间复杂度为O(n^2),与冒泡排序相同。因此,不能说插入排序总是比冒泡排序低
8、在C++ 中,如果没有捕获到异常(没有匹配的catch 块),程序会继续执行而不会终止。
答案:错误
解析:在C++ 中,如果抛出了异常但没有找到匹配的catch 块来捕获它,程序会调用std::terminate(),导致程序终止。因此,未捕获的异常会导致程序结束,而不是继续执行
9、以下代码用递推法求斐波那契数列的第n项,时间复杂度为指数级。

答案:错误
解析:迭代的方式计算斐波那契数列,时间复杂度为线性O(n),而不是指数级O(2^n)
10、执行下面C++代码后,输出的是20。

答案:正确
解析:通过指针传递,函数point(p) 返回10 * 2 = 20,并将其赋给a,最终输出20
三、编程题(每题25分,共50分)
|
题号 |
1 |
2 |
|
答案 |
1、黑白方块
题面描述
小杨有一个n行m列的网格图,其中每个格子要么是白色,要么是黑色。小杨想知道网格图中是否存在一个满足如下条件的子矩形:
子矩形由4行4列组成;
子矩形的第1行和第4行只包含白色格子;
对于子矩形的第2行和第3行,只有第1个和第4个格子是白色的,其余格子都是黑色的;
请你编写程序帮助小杨判断。
输入格式
第一行包含一个正整数t,代表测试用例组数。
接下来是t组测试用例。对于每组测试用例,一共+1行。
第一行包含两个正整数n,m,含义如题面所示。
之后n行,每行一个长度为m的01串,代表网格图第行格子的颜色,如果为0,则对应格子为白色,否则为黑色。
输出格式
对于每组测试用例,如果存在,输出Yes,否则输出No。
样例1

满足条件的子矩形形如:

对于全部数据,保证有1 ≤ t ≤ 10,1 ≤ n,m ≤ 100。
【解题思路】
1. 定义二维数组,存储将要输入的网格数据;
2. 将要匹配的模版矩形初始化,4X4的二维数组;
3. 使用循环遍历网格数据中每一个可能的4X4子矩形,检查从位置(i,j)开始的子矩形是否与之前定义好的模版匹配
进行判断,输出结果
【参考程序】

2、区间排序
题面描述
小杨有一个包含n个正整数的序列a。
小杨计划对序列进行多次升序排序,每次升序排序小杨会选择一个区间[l,r],(l ≤ r)并对区间内所有数字,即ai, ai+1,..., ar进行升序排序。每次升序排序会在上一次升序排序的结果上进行。
小杨想请你计算出多次升序排序后的序列。
输入格式
第一行包含一个正整数n,含义如题面所示。
第二行包含几个正整数a1, a2,..., an,代表序列。
第三行包含一个正整数q,代表排序次数。
之后q行,每行包含两个正整数li,ri,代表将区间[li,ri]内所有数字进行升序排序。
输出格式
输出一行包含n个正整数,代表多次升序排序后的序列。
样例1

第一次升序排序后,序列为[3,4,5,1,2];
第二次升序排序后,序列为(3,4,1,5,2];
第三次升序排序后,序列为[1,3,4,5,2];
对于全部数据,保证有1 ≤ n ≤ 100, 1 ≤ ai≤ 100,1 ≤ q ≤ 100,1 ≤ li≤ ri ≤ n。
【解题思路】
1. 将要排序的数据存储在列表中,初始化定义一个长度为1010的数组,尽可能大的数据,确保在输入规模较大的情况下不会越界
2. 定义排序方法对数据进行排序
3. 处理部分:
4. 存储输入数据
5. 处理需要进行排序的区间操作数q
6. 对选定的l,r区间范围内数据进行排序操作,调用之前定义好的排序方法
7. 输出结果
【参考程序】

技术支持:郝利斌
策划:GESP技术委员会副主席 刘晓庆


1. GESP微信:关注“CCF GESP”公众号,点击“GESP小助手”即可交流。
2. GESP邮箱:gesp@ccf.org.cn
注:请在邮件中详细描述咨询的问题并留下考生的联系方式及姓名、身份证号,以便及时有效处理。
3. GESP电话:0512-67656856
咨询时间:周一至周五(法定节假日除外)上午 8:30-12:00;下午 13:00-17:30

扫码关注GESP公众号,了解更多资讯


点击此处 “阅读原文” 查看更多内容


