Bootstrap

2024年9月GESPC++四级真题解析

一、单选题(每题2分,共30分)

题目123456789101112131415
答案

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

【考纲知识点】C++ 函数的定义和返回值类型。

【解析】
A 选项:int add(int a, int b) { return a + b; } 正确定义了一个返回整数值并接受两个整数参数的函数。函数返回类型为 int ,接受两个 int 类型的参数 a 和 b ,并返回它们的和。

B 选项:void 表示函数不返回任何值,但这里却有 return a + b; 试图返回一个值,这是错误的。

C 选项:参数 a 和 b 没有指定类型,不符合函数定义的规范。

D 选项:函数返回类型为 void ,不能有返回值,但这里却有 return a - b; ,这是错误的。

综上所述,正确的是 A 选项。

2. 在C++中,形参与实参的关系描述正确的是( )。

A. 形参在函数调用时指定,实参在函数定义时传递

B. 形参在函数定义时指定,实参在函数调用时传递

C. 形参和实参可以互换

D. 形参和实参必须是完全相同的类型,不能有任何差异。

【答案】B

【考纲知识点】C++ 中形参和实参的概念及关系。

【解析】
在 C++ 中,形参是在函数定义时指定的变量,用于接收调用函数时传递的实参的值。

实参是在函数调用时传递给函数的具体值或变量。

A 选项说法错误,形参在函数定义时指定,实参在函数调用时传递。

C 选项说法错误,形参和实参有明确的定义和作用,不能互换。

D 选项说法错误,形参和实参不要求必须完全相同类型,但需要类型兼容,允许进行适当的类型转换。

综上所述,B 选项的描述正确。

3. 运行以下代码,屏幕上将输出( )。
#include <iostream>

using namespace std;

int var = 100;

void function() {

int var = 200;

cout << var << " ";

cout << ::var << " ";

}

int main() {

cout << var << " ";

function();

var += 100;

cout << var << " ";

return 0;

}

A. 100 200 100 200

B. 100 200 100 300

C. 100 200 200 200

D. 100 200 200 300

【答案】A

【考纲知识点】C++ 中全局变量和局部变量的作用域。

【解析】
在 main 函数中,首先输出全局变量 var ,其值为 100 。

在 function 函数中,定义了一个局部变量 var 并赋值为 200 ,当输出 var 时,输出的是局部变量的值 200 。而 ::var 表示访问全局变量 var ,其值仍为 100 。

回到 main 函数,全局变量 var 未被修改,仍为 100 。然后 var += 100 ,使其变为 200 。

所以最终的输出结果是 100 200 100 200 ,选择 A 选项。

4.运行下面代码,屏幕上输出是( )。
int arr[3] = {24, 9, 7};

int* p = arr;

p++;

cout << *p << endl;

A. 24

B. 9

C. 7

D. 不确定

【答案】B

【考纲知识点】指针与数组的关系。

【解析】

在 C++ 中,当定义 int* p = arr; 时,p 指向数组 arr 的首元素。

执行 p++; 操作后,p 指针向后移动一个位置,此时 p 指向了数组的第二个元素,即 9 。

然后通过 cout << *p << endl; 输出 p 所指向的元素的值,也就是 9 。

所以选择 B 选项。
5.运行下面代码片段的结果是( )。

int x = 20;

int y = 24;

int* p = &x;

int* q = &y;

p = q;
A.将x赋值为24

B.将y赋值为20

C.将q指向x的地址

D.将p指向y的地址

【答案】D

【考纲知识点】指针的操作和地址的赋值。

【解析】
在这段代码中,首先定义了两个整数 x 和 y ,并分别为它们赋值。然后定义了两个指针 p 和 q ,分别指向 x 和 y 的地址。

当执行 p = q; 时,是将指针 p 所指向的地址修改为指针 q 所指向的地址,也就是让 p 指向了 y 的地址。

A 选项,没有对 x 进行赋值操作。

B 选项,没有对 y 进行赋值操作。

C 选项,不是将 q 指向 x 的地址,而是将 p 指向了 q 所指向的地址,即 y 的地址。

综上所述,选择 D 选项。

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

【考纲知识点】C++ 中结构体的定义。

【解析】
在 C++ 中,结构体的定义格式为:struct 结构体名 { 成员列表 }; 。

A 选项:struct student { char name[20]; int age; }; ,按照正确的格式定义了名为 student 的结构体,其中包含一个长度为 20 的字符数组 name 和一个整数 age ,是正确的定义方式。

B 选项:student struct { char name[20]; int age; }; ,结构体定义的格式错误,struct 关键字应该在结构体名称之前。

C 选项:首先,直接使用 string 类型需要包含 <string> 头文件。其次,这里的结构体定义格式也是错误的。

D 选项:char[20] name; 这种语法是错误的,应该是 char name[20]; 。

综上所述,选择 A 选项。

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

【考纲知识点】C++ 中二维数组的声明方式。

【解析】
在 C++ 中,声明二维数组的正确语法是 数据类型 数组名[行数][列数] 。

A 选项:int arr[3, 4]; 这种语法是错误的,不能使用逗号来声明二维数组的大小。

B 选项:int arr[3][4]; 这是正确的声明一个 3 行 4 列的二维数组的方式。

C 选项:int arr[4][3]; 声明的是一个 4 行 3 列的二维数组,不符合题目要求的 3 行 4 列。

D 选项:int arr(3, 4); 这种语法不是 C++ 中声明二维数组的正确方式。

综上所述,选择 B 选项。

8. 一个二维数组定义为 int arr[3][4]; (假设一个int变量占4个字节),则 int arr[0] 占用( )个字节的内存。

A. 3

B. 4

C. 12

D. 16
【答案】D

【考纲知识点】二维数组的存储和内存占用计算。

【解析】
在定义为 int arr[3][4]; 的二维数组中,int arr[0] 表示二维数组的第一行。

因为每行有 4 个 int 类型的元素,每个 int 类型占用 4 个字节,所以第一行总共占用的内存为:4×4=16 字节
综上所述,选择 D 选项。

9.下面代码采用递推算法来实现整数n的阶乘(n!=n×(n-1)....×2×1 ),则横线上应填写( )。
int factorial(int n) {

int result = 1;

for (int i = 2; i <= n; i++) {

________________________________ // 在此处填入代码

}

return result;

}

A. result *= i;

B. result += i;

C. result *= result;

D. result += result;

【答案】A

【考纲知识点】递推算法和循环的应用。

【解析】阶乘的计算是从 1 开始依次乘以 2、3、4…… 直到 n 。

在这个循环中,result 初始值为 1 ,从 i = 2 开始,每次都应该将当前的 result 乘以 i ,以逐步计算阶乘。

A 选项:result *= i; ,能够实现每次将 result 乘以当前的 i ,从而逐步计算出阶乘的值。

B 选项:result += i; 是进行加法操作,不符合阶乘的乘法运算规则。

C 选项:result *= result; 只是将 result 自身相乘,无法实现乘以从 2 到 n 的递增整数。

D 选项:result += result; 同样是加法操作,不符合阶乘的计算要求。

综上所述,横线上应填写 A 选项 result *= i;

10.在排序算法中,稳定性指的是( )。

A. 排序后数据不会丢失

B. 排序后相同元素的相对顺序保持不变

C. 排序后数据不会被修改

D. 排序后数据的时间复杂度不变

【答案】B

【考纲知识点】排序算法中稳定性的概念。

【解析】
在排序算法中,稳定性指的是:在排序过程中,如果两个元素的关键字相等,排序前它们的相对顺序和排序后它们的相对顺序相同。

A 选项,排序后数据不会丢失,这不是稳定性的定义。

C 选项,排序后数据是否被修改不是稳定性所关注的,稳定性关注的是相等元素的相对顺序。

D 选项,时间复杂度不变与稳定性毫无关系,时间复杂度是衡量算法效率的指标。

综上所述,选择 B 选项。

11.下面代码实现了冒泡排序函数,则横线上应填写( )。
//交换数组arr的第i个元素和第j个元素

void swap(vector<int> &arr, int i, int j) {

int tmp = arr[i];

arr[i] = arr[j];

arr[j] = tmp;

}

int bubble_sort(vector<int> &arr) {

for (int i = arr.size() - 1; i > 0; i--) {

bool flag = false; // 标志位

________________________________ { // 在此处填入代码

if (arr[j] > arr[j + 1]) {

swap(arr, i, j);

flag = true;

}

}

if (!flag)

break; // 此轮“冒泡”未交换任何元素

}

}

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

【考纲知识点】冒泡排序算法的实现。

【解析】
冒泡排序的基本思想是通过相邻元素的比较和交换,将最大的元素逐步 “冒泡” 到数组的末尾。

在外层循环中,i 从数组的末尾开始逐渐向前移动,控制每一轮比较的范围。

内层循环应该从数组的开头开始,比较到当前轮次的倒数第二个元素,即 j < i 。

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++) 是正确的,能够在当前轮次 i 控制的范围内进行相邻元素的比较和交换。

D 选项:for (int j = i - 1; j <= 0; j--) 比较范围和方向都不正确。

综上所述,横线上应填写 C 选项。

12.上一题算法的时间复杂度为( )。

A.O(n2)

B.O(2n)

C.O(1)

D.O(n)

【答案】A

【考纲知识点】算法时间复杂度分析。

【解析】
冒泡排序的基本操作是比较和交换相邻的元素。

在最坏情况下,对于一个长度为 n 的数组,外层循环需要执行 n - 1 次,内层循环在每一轮中需要执行的次数逐渐减少,但总体上内层循环的执行次数也接近 n 次。

所以总的比较和交换操作的次数接近 n × (n - 1) / 2 ,时间复杂度为 O (n²)。

综上所述,选择 A 选项。

13.下面代码实现了插入排序函数(升序),则横线上应填写( )。
void insertion_sort(vector<int> &nums) {

for (int i = 1; i < nums.size(); i++) {

int base = nums[i], j = i - 1;

________________________________ { // 在此处填入代码

nums[j + 1] = nums[j];

j--;

}

nums[j + 1] = base;

}

}

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

【考纲知识点】插入排序算法的实现。

【解析】插入排序的工作方式是:每次将一个待排序的元素,插入到前面已经排好序的部分的适当位置。

当我们要将 nums[i] 插入到已排序的 nums[0] 到 nums[i - 1] 中时,需要从 nums[i - 1] 开始向前比较,如果 nums[j] 大于当前基准值 base ,就将其向后移动一位,直到找到合适的位置插入 base 。所以条件应该是 while (j >= 0 && nums[j] > base) 。

A 选项正确,确保了在不越界的情况下,只要前面的元素大于基准值就继续移动。

B 选项 j > 0 可能导致越界。

C 选项比较条件错误,应该是大于基准值才移动。

D 选项比较条件错误且 j > 0 可能导致越界。

综上所述,选择 A 选项。

14.小杨用文件重定向实现在 log.txt 文件中输出日志,则下面横线上应填写( )。
#include <iostream>

#include <fstream>

#include <string>

using namespace std;

int main() {

ofstream log_file("log.txt");

streambuf* original_cout = cout.rdbuf();

cout.rdbuf(log_file.rdbuf());

___________________________________ // 在此处填入代码

cout.rdbuf(original_cout); // 恢复原始的标准输出缓冲区

return 0;

}

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 ,所以要在 log.txt 文件中输出日志,应该使用 cout 。

A 选项:cout << "This output will go to the log file." << endl; ,使用了重定向后的 cout 进行输出,信息将被写入到文件中。

B 选项:log_file 是文件流对象,直接使用它输出不符合这里的重定向逻辑。

C 选项:>> 运算符用于输入流,这里是输出,使用错误。

D 选项:同 B 选项,且 >> 用于输入,错误。

综上所述,选择 A 选项。

15.运行下面的代码,屏幕上将输出( )。
#include <iostream>

using namespace std;

int divide(int a, int b) {

if (b == 0) {

throw runtime_error("division by zero error ");

}

return a / b;

}

int main() {

int x = 10;

int y = 0; // 设为 0 会导致除零错误

try {

int result = divide(x, y);

cout << "result: " << result << endl;

} catch (const runtime_error& e) {

cout << "caught an exception: " << e.what() << endl;

}

return 0;

}

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) 会因为除数为 0 抛出异常。

抛出的异常被 catch 块捕获,然后输出异常的信息。

C 选项:caught an exception: division by zero error ,符合异常被捕获并输出异常信息的情况。

A 选项:不会先输出错误信息再输出 result 。

B 选项:不会先输出 result 再输出异常信息。

D 选项:输出格式错误。

综上所述,选择 C 选项。

二、判断题(每题2分,共20分)

题目12345678910
答案

×

×

×

×

×

×

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项,时间复杂度为指数级。
int fibonacci(int n) {

if (n == 0) return 0;

if (n == 1) return 1;

int f0 = 0; // F(0)

int f1 = 1; // F(1)

int current;

for (int i = 2; i <= n; i++) {

return current

current = f0+ f1; // F(n) = F(n-1) + F(n-2)

f0 = f1;

f1 = current;

}

return current;

}

【答案】错误

【考纲知识点】斐波那契数列的计算时间复杂度。

【解析】这里使用的是迭代的方法计算斐波那契数列,通过循环从第 2 项逐步计算到第 n 项,时间复杂度为 O (n) 。而如果是递归方式计算,可能会出现大量的重复计算,导致时间复杂度达到指数级。

10.执行下面C++代码后,输出的是20。
int point(int* p){

return *p * 2;

}

int main() {

int a = 10;

int* p = &a;

*p = point(p);

cout << *p << endl;

}

【答案】正确

【考纲知识点】指针与函数调用。

【解析】在 point 函数中,通过 *p 得到指针所指向的值并乘以 2 返回。在 main 函数中,将指针 p 传递给函数,函数返回值 20 又通过 *p 赋值给了指针所指向的变量 a ,所以最终输出 *p 即 a 的值为 20 。

三、编程题(每题25分,共50分)

1、黑白方块

【问题描述】

小杨有一个n行m列的网格图,其中每个格子要么是白色,要么是黑色。小杨想知道网格图中是否存在一个满足如下条件的子矩形:
子矩形由4行4列组成;

子矩形的第1行和第4行只包含白色格子;

对于子矩形的第2行和第3行,只有第1个和第4个格子是白色的,其余格子都是黑色的;
请你编写程序帮助小杨判断。

【输入描述】

第一行包含一个正整数t,代表测试用例组数。

接下来是t组测试用例。对于每组测试用例,一共+1行。

第一行包含两个正整数n,m,含义如题面所示。

之后n行,每行一个长度为m的01串,代表网格图第行格子的颜色,如果为0,则对应格子为白色,否则为黑色。

【输出描述】

对于每组测试用例,如果存在,输出Yes,否则输出No。

【样例输入 1】

3

1 4

0110

5 5

00000

01100

01100

00001

01100

5 5

00000

01100

01110

00001

01100

【样例输出 1】

No

Yes

No

【题目大意】

给定若干个由 0 和 1 组成的 n 行 m 列的网格图,判断每个网格图中是否存在一个特定形状的 4 行 4 列的子矩形,该子矩形的第一行和第四行全是白色(0),第二行和第三行只有第一个和第四个格子是白色(0),其余是黑色(1)。

【考纲知识点】
1.二维数组的使用和操作。

2.循环遍历和条件判断的综合运用。

【解题思路】

1.首先,使用二维数组来存储输入的网格图数据。

2.然后,定义一个同样大小的二维数组来表示需要匹配的模板子矩形。

3.通过两层嵌套的循环遍历输入的网格图,每次从一个位置开始,提取一个 4 行 4 列的子矩形。

4.将提取的子矩形与模板子矩形进行逐个元素的比较,如果完全匹配则表示存在符合条件的子矩形,立即结束遍历并输出结果。

5.如果遍历完整个网格图都没有找到匹配的子矩形,则输出不存在。
【参考程序】
#include<bits/stdc++.h>

using namespace std;

const int N = 110;

int w[N][N];

int n,m;

int match[4][4];

bool check(int xa,int ya){

for(int i=0;i<4;i++){

for(int j=0;j<4;j++){

if(w[xa+i][ya+j]!=match[i][j]){

return false;

}

}

}

return true;

}

int main(){

int t;

cin>>t;

for(int i=1;i<3;i++)match[1][i]=match[2][i]=1;

while(t--){

cin>>n>>m;

for(int i=1;i<=n;i++){

string s;

cin>>s;

for(int j=1;j<=m;j++){

w[i][j]=s[j-1]-'0';

}

}

int fl = 0;

for(int i=1;i<=n-3;i++){

for(int j=1;j<=m-3;j++){

if(check(i,j)){

fl=1;

}

}

}

if(fl)cout<<"Yes\n";

else cout<<"No\n";

}

}

2、区间排序

【问题描述】

小杨有一个包含n个正整数的序列a。

小杨计划对序列进行多次升序排序,每次升序排序小杨会选择一个区间[l,r],(l ≤ r)并对区间内所有数字,即ai, ai+1,..., ar进行升序排序。每次升序排序会在上一次升序排序的结果上进行。

小杨想请你计算出多次升序排序后的序列。

【输入描述】

第一行包含一个正整数n,含义如题面所示。

第二行包含几个正整数a1, a2,..., an,代表序列。

第三行包含一个正整数q,代表排序次数。

之后q行,每行包含两个正整数li,ri,代表将区间[li,ri]内所有数字进行升序排序。

【输出描述】

输出一行包含n个正整数,代表多次升序排序后的序列。

【样例输入 1】

5

3 4 5 2 1

3

4 5

3 4

1 3

【样例输出 1】

1 3 4 5 2

【题目大意】

给定一个包含 n 个正整数的序列,以及 q 次排序操作,每次操作指定一个区间 [l, r],对该区间内的数字进行升序排序,最终得到多次排序后的序列。

【考纲知识点】
1.数组的操作和使用。

2.排序算法的理解和应用。

【解题思路】

1. 将要排序的数据存储在列表中,初始化定义一个长度为1010的数组,尽可能大的数据,确保在输入规模较大的情况下不会越界

2. 定义排序方法对数据进行排序

3. 处理部分:

4. 存储输入数据

5. 处理需要进行排序的区间操作数q

6. 对选定的l,r区间范围内数据进行排序操作,调用之前定义好的排序方法

7. 输出结果

【参考程序】
#include<bits/stdc++.h>

using namespace std;

const int N = 1010;

int a[N];

int n;

void bubbleSort(int l,int r){

bool flag = true;

while (flag) {

flag = false;

for (int i = l; i <r; ++i) {

if (a[i] > a[i + 1]) {

flag = true;

swap(a[i],a[i+1]);

}

}

}

}

int main(){

cin>>n;

for(int i=1;i<=n;i++){

cin>>a[i];

}

int q;

cin>>q;

while(q--){

int l,r;

cin>>l>>r;

bubbleSort(l,r);

}

for(int i=1;i<=n;i++){

cout<<a[i];

if(i!=n)cout<<" ";

else cout<<"\n";

}

}

;