GESP 客观题评测系统

2025年06月 C++ 四级

2025-06-Level-4

试卷解析总览,可直接查看每题答案与解析。

单选题(每题 2 分)

1 题(单选题

在C++中,声明一个指向整型变量的指针的正确语法是()。

A.
int* ptr;
B.
*int ptr;
C.
int ptr*;
D.
ptr int;

正确答案A

解析详情

【答案】A

【考点】指针声明语法

【解析】 在 C++ 中,声明指针的语法为“类型* 变量名;”。int* ptr; 表示声明一个指向 int 类型变量的指针。星号(*)紧跟在类型之后或变量名之前均可,但位置必须在两者之间。

【易错点】 误将星号置于类型之前(如 *int)或变量名之后(如 ptr*)。

2 题(单选题

下面的函数接收一个3行4列的二维数组并输出其中元素,则横线上不能填写()。

void printArray(____) {
    for (int i = 0; i < 3; ++i)
        for (int j = 0; j < 4; ++j)
            std::cout << arr[i][j] << " ";
}
A.
int arr[3][4]
B.
int arr[][4]
C.
int (*arr)[4]
D.
int** arr

正确答案D

解析详情

【答案】D

【考点】二维数组参数传递

【解析】 二维数组作为函数参数时,必须指定第二维(列数)的大小,以便编译器计算内存偏移量。int** arr 是二级指针,指向指针的指针,与二维数组在内存中按行连续存储的物理布局不兼容。

【易错点】 混淆二级指针与数组指针(如 int (*arr)[4])在传递二维数组时的区别。

3 题(单选题

在C++中,int arr[3][4] 和 int* arr = new int[12] 均可模拟一个3行4列的二维数组。关于这两种方式,下面说法错误的是()。

A.
int arr[3][4] 在栈上分配空间,适合数组较小的情况;
B.
int* arr = new int[12] 在堆上分配空间,数组较大时也适用;
C.
这两种方式申请的内存空间都是连续的。
D.
这两种方式申请的内存都能自动释放。

正确答案D

解析详情

【答案】D

【考点】内存管理(栈与堆)

【解析】 静态数组 int arr[3][4] 在栈上分配,作用域结束由系统自动释放。而使用 new 申请的动态内存位于堆上,必须使用 delete[] 显式手动释放,否则会导致程序运行期间出现内存泄漏。

【易错点】 错误认为所有分配出的内存(包括 new 申请的堆内存)都会在函数结束时自动回收。

4 题(单选题

关于以下 C++代码,说法正确的是()。

int main() {
    greet();
    return 0;
}

void greet() {
    cout << "Hello!" << endl;
}
A.
正确编译并输出 Hello!
B.
编译错误:找不到函数 greet()
C.
编译警告但可以运行
D.
链接错误

正确答案B

解析详情

【答案】B

【考点】函数原型声明

【解析】 C++ 遵循“先声明后使用”的原则。在代码中,greet() 的定义出现在 main 函数调用之后,且在调用前没有提供函数原型声明,编译器在处理 main 时无法识别该标识符,导致编译错误。

【易错点】 误以为只要函数在同一个源文件中定义,无论物理位置在调用处之前还是之后都能直接运行。

5 题(单选题

在 C++ 中,如果希望通过函数修改传入的结构体对象的内容,应该使用哪种参数传递方式?

A.
值传递或引用传递
B.
值传递或指针传递
C.
引用传递或指针传递
D.
仅指针传递

正确答案C

解析详情

【答案】C

【考点】参数传递与副作用

【解析】 值传递会创建对象的副本,函数内部对副本的修改无法反馈给原始对象。若要修改原始结构体,必须传递对象的地址(指针传递)或别名(引用传递),使函数能够直接操作原始内存空间。

【易错点】 误认为值传递可以改变实参的值,或不清楚引用传递相较于值传递在修改数据方面的核心差异。

6 题(单选题

以下哪个选项正确描述了 C++ 中形参和实参的区别?

A.
形参是函数调用时传递给函数的具体值,实参是函数定义中声明的变量。
B.
形参是函数定义中声明的变量,实参是函数调用时传递给函数的具体值。
C.
形参和实参在函数调用时是完全相同的。
D.
形参只在函数内部可见,实参在函数外部可见。

正确答案B

解析详情

【答案】B

【考点】形参与实参概念

【解析】 形参是函数定义时在括号内声明的变量,仅作为占位符使用,只有在调用时才分配内存;实参是函数调用时传递给函数的具体常量、变量或表达式的值,用于初始化形参。

【易错点】 记反形参与实参的定义位置(如认为调用时传入的是形参)。

7 题(单选题

运行如下代码会输出()。

int value = 100;
void print1() {
    int value = 50;
    cout << value << " ";
    cout << ::value << " ";
}

void print2() {
    cout << value << " ";
}

print1();
print2();
A.
100 100 100
B.
50 50 50
C.
50 100 100
D.
50 50 100

正确答案C

解析详情

【答案】C

【考点】作用域与全局解析符 (::)

【解析】 print1 中局部变量 value(50) 屏蔽了同名全局变量,故首个输出为 50;紧接着使用 ::value 强制访问全局作用域中的变量,输出 100。print2 中无局部同名变量,直接访问全局变量输出 100。序列为 50 100 100。

【易错点】 忽略了 :: 符号对全局作用域变量的强制访问作用,或者误认为局部变量的修改会永久改变全局变量。

8 题(单选题

小杨在整理一副扑克牌的所有红心扑克牌,使其从小到大排列。他的做法是:最开始抓到第1张扑克牌被认为已经排好序;然后抓第2张扑克牌,将其插入至有序部分的正确位置;不断循环步骤,每次将新抓到扑克牌插入至有序部分,直至抓完所有扑克牌,这样抓牌结束时就完成了扑克牌的排序。小杨这种整理扑克牌的方式与()排序的方式最接近。

A.
冒泡排序
B.
插入排序
C.
选择排序
D.
直接排序

正确答案B

解析详情

【答案】B

【考点】排序算法核心思想

【解析】 小杨的操作是先视第一张牌有序,后续每抓一张牌都将其插入到前面已排序序列的正确位置。这完全符合“插入排序”每一步将一个待排序元素按值大小插入到已排序部分适当位置的核心逻辑。

【易错点】 将“插入到有序部分”的动作误认为“选择最小元素交换”的选择排序。

9 题(单选题

以下哪种情况是使用插入排序的合适场景?

A.
数据量非常大,且乱序严重
B.
希望获得稳定排序,但不要求实时性
C.
数据几乎有序,只需少量调整
D.
想在交换次数最少的前提下排好大数组

正确答案C

解析详情

【答案】C

【考点】插入排序的性能特点

【解析】 插入排序在数据“几乎有序”的情况下,每个待插入元素只需进行极少次比较和移动即可归位,此时时间复杂度接近 O(n),效率极高。对于大规模乱序数据,其表现通常不如 O(n log n) 级别的算法。

【易错点】 忽略算法性能与输入数据初始分布(如有序程度)之间的密切关系。

10 题(单选题

以下关于递推算法基本思想的描述,正确的是()。

A.
递推算法通过将问题分解为相互独立的子问题来解决。
B.
递推算法从已知的基础情况出发,通过某种关系逐步推导出更大规模问题的解。
C.
递推算法通常用于穷举所有可能的解决方案。
D.
递推算法适用于在每一步做出局部最优选择以达到全局最优。

正确答案B

解析详情

【答案】B

【考点】递推算法定义

【解析】 递推算法是从已知的初始条件(边界情况)出发,利用确定的关系式(递推公式)由前向后、由小到大逐一推导出后续各项值的过程。选项 A 是分治思想,C 是枚举思想,D 是贪心思想。

【易错点】 混淆递推(自底向上计算)与递归(自顶向下分解并最终合并)的执行逻辑流向。

11 题(单选题

给定如下算法,其时间复杂度为()。

bool f(int arr[], int n, int target) {
    for (int i = 0; i < n; i++) {
        int sum = 0;
        for (int j = 0; j < n; j++) {
            if (i & (1 << j)) {
                sum += arr[j];
            }
        }
        if (sum == target) return true;
    }
    return false;
}
A.
O(n)O(n)
B.
O(n2)O(n^2)
C.
O(n3)O(n^3)
D.
O(2n)O(2^n)

正确答案B

解析详情

【答案】B

【考点】复杂度分析

【解析】 外层循环 for(i=0; i<n; i++) 执行 n 次,内层循环 for(j=0; j<n; j++) 同样执行 n 次。循环体内位运算和加法均为 O(1) 基本操作。总基本操作次数与 n * n 成正比,故时间复杂度为 O(n²)。

【易错点】 看到位移运算 1 << j 误认为是子集枚举的 O(2ⁿ),需注意外层循环的终止条件是 i < n 而非 i < (1 << n)。

12 题(单选题

下述斐波那契数列计算的时间复杂度是()。

int fibonacci(int n) {
    if (n == 0) return 0;
    if (n == 1) return 1;
    return fibonacci(n - 1) + fibonacci(n - 2);
}
A.
O(n)O(n)
B.
O(n2)O(n^2)
C.
O(n3)O(n^3)
D.
O(2n)O(2^n)

正确答案D

解析详情

【答案】D

【考点】递归复杂度分析

【解析】 该实现为朴素递归,计算 fib(n) 会产生两个子递归 fib(n-1) 和 fib(n-2),形成一个深为 n 的递归调用树。由于存在大量重复计算,调用节点的总数随 n 指数级增长,复杂度为 O(2ⁿ)。

【易错点】 误认为递归深度为 n 复杂度即为 O(n),忽略了每一次调用都会产生分支带来的爆炸式增长。

13 题(单选题

关于下面 C++ 程序的描述,( )最准确。

ifstream in("data.txt");
string line;
while (getline(in, line)) {
    cout << line << endl;
}
A.
将从标准输入读取每行,并输出到屏幕
B.
程序无法运行,因为 getline 只能读取 cin
C.
将 data.txt 中的每一行读取并输出到屏幕
D.
程序将创建 data.txt 并写入默认文本

正确答案C

解析详情

【答案】C

【考点】文件输入流操作 (ifstream)

【解析】 代码逻辑:首先通过 ifstream in("data.txt") 打开名为 data.txt 的文件。接着 while(getline(in, line)) 会逐行读取文件内容直到末尾,并将读到的每一行通过 cout 打印到屏幕。对应选项 C。

【易错点】 混淆 ifstream(输入/读取)与 ofstream(输出/写入)的功能差异。

14 题(单选题

在C++中,异常处理机制(try-catch块)的主要目的是()。

A.
提高程序的运行速度。
B.
在程序发生运行时错误时,提供一种结构化的错误处理方式。
C.
确保程序在编译时没有错误。
D.
减少程序的内存占用。

正确答案B

解析详情

【答案】B

【考点】异常处理机制初衷

【解析】 异常处理(try-catch)旨在捕获程序在运行期间发生的意外错误(如除零、文件丢失等),并将错误检测逻辑与错误处理逻辑分离。通过这种结构化方式,程序可以尝试从错误中恢复或安全退出,提高健壮性。

【易错点】 误以为异常处理能解决逻辑设计错误或编译时期的语法错误。

15 题(单选题

为了提高冒泡排序的效率,如果某轮“冒泡”中没有执行任何交换操作,说明数组已经完成排序,可直接返回结果,则两条横线上分别应该填写()。

void bubbleSortWithFlag(vector<int> &nums) {
    for (int i = nums.size() - 1; i > 0; i--) {
        bool flag;
        // 1
        for (int j = 0; j < i; j++) {
            if (nums[j] > nums[j + 1]) {
                swap(nums[j], nums[j + 1]);
                // 2
            }
        }
        if (!flag) {
            break;
        }
    }
}
A.
flag = false;
flag = false;
B.
flag = false;
flag = true;
C.
flag = true;
flag = false;
D.
flag = true;
flag = true;

正确答案B

解析详情

【答案】B

【考点】冒泡排序优化标志位

【解析】 标志位优化的核心是检测本轮是否发生交换。在每轮外层循环开始前,需将 flag 重置为 false(假设有序)。若内层循环中发生了 swap 交换,则设 flag 为 true。若一轮结束 flag 仍为 false,说明整轮无交换,数组已完全有序。

【易错点】 标志位的布尔逻辑(false/true)写反,或标志位的重置位置错误。

判断题(每题 2 分)

1 题(判断题

下面 C++ 代码正确声明了一个返回 int 类型、接受两个 int 参数的函数。

int add(int, int);

正确答案正确

解析详情

【答案】正确

【考点】函数声明语法

【解析】 在 C++ 中,函数声明(原型)只需指明返回类型、函数名以及参数类型列表。参数的具体变量名在声明阶段是可选的。因此,int add(int, int); 是完全符合语法规范的函数原型。

【易错点】 误认为函数声明必须像函数定义那样显式写出参数的变量名(如 a, b)。

2 题(判断题

下面 C++ 代码的输出是 15。

void foo(int x) {
    x += 5;
}
int main() {
    int a = 10;
    foo(a);
    cout << a << endl;
}

正确答案错误

解析详情

【答案】错误

【考点】值传递的作用域

【解析】 该代码采用值传递(Pass by Value),函数 foo 内修改的是实参 a 的副本 x。函数返回后,main 函数中的变量 a 本身没有受到任何影响。因此 cout << a 输出的值仍为 10。

【易错点】 混淆了值传递与引用传递(如 int &x)或指针传递在副作用上的核心区别。

3 题(判断题

下面 C++ 代码在一个结构体中又定义了别的结构体。这种结构嵌套定义的方式语法不正确。

#include <string>
#include <vector>

using namespace std;

struct Library {
    struct Book {
        struct Author {
            string name;
            int birthYear;
        };
        string title;
        int year;
        Author author;
    };
};

正确答案错误

解析详情

【答案】错误

【考点】结构体定义规范

【解析】 C++ 语法完全允许在结构体内部嵌套定义另一个结构体。这种方式通常用于表示明确的层级或从属关系,使得数据组织更加严密。代码中的三层嵌套结构在语法上是合法的。

【易错点】 误认为结构体必须全部定义在全局作用域或平级位置,忽略了嵌套类的语言特性。

4 题(判断题

在C++中,相比于值传递,使用引用传递作的优点可以直接操作和修改原始变量,避免数据拷贝,提高效率。

正确答案正确

解析详情

【答案】正确

【考点】引用传递优势

【解析】 引用传递直接向函数传递实参变量的别名,不产生对象副本,避免了拷贝大型结构体带来的时空开销。同时,它允许函数内部通过引用直接访问并修改实参的内存单元,描述准确。

【易错点】 虽然高效,但需注意引用传递若不加 const,函数内的意外修改会直接影响调用方的数据。

5 题(判断题

下面这段代码不合法,因为每一行都必须显式初始化 3 个元素。

int arr[2][3] = {{1, 2}, {3}};

正确答案错误

解析详情

【答案】错误

【考点】二维数组初始化规则

【解析】 在 C++ 中,二维数组初始化时如果提供的初始值数量少于数组容量,编译器会自动为剩余位置补 0。int arr[2][3] = {{1, 2}, {3}}; 是合法的,未明确给出的位置(如 arr[0][2] 等)都会被置 0。

【易错点】 混淆了“维数大小”与“初始化列表长度”的关系,误认为必须填满每一个初始槽位。

6 题(判断题

以下程序中使用了递推方式计算阶乘(n!=1×2×nn! = 1 \times 2 \ldots \times n),计算结果正确。

int factorial(int n) {
    int res = 1;
    for (int i = 0; i < n; ++i) {
        res *= i;
    }
    return res;
}

正确答案错误

解析详情

【答案】错误

【考点】循环边界与逻辑初值

【解析】 代码中的循环变量 i 从 0 开始。在第一次迭代执行 res *= i; 时,由于 0 乘以任何数都得 0,变量 res 会立刻变成 0 并保持到结束。正确的阶乘累乘应当从 i = 1 开始或使乘数不包含 0。

【易错点】 在编写累乘逻辑时,习惯性地将循环起点设为 0 而忽略了 0 在乘法中的归零作用。

7 题(判断题

无论初始数组是否有序,选择排序都执行O(n2)O\left(n^{2}\right)次比较

正确答案正确

解析详情

【答案】正确

【考点】选择排序复杂度稳定性

【解析】 选择排序的算法逻辑决定了每一轮都必须扫描完剩余的所有元素以确定最小值的位置。无论输入数据是已经有序、逆序还是随机,它的比较次数始终是固定的 n(n-1)/2 次,时间复杂度恒定为 O(n²)。

【易错点】 误认为选择排序像冒泡或插入排序一样,在数据有序时会自动优化到更低的时间复杂度。

8 题(判断题

以下C++代码,尝试对有n个整数的数组arr进行排序。这个代码实现了选择排序算法。

for (int i = 0; i < n - 1; ++i) {
    int minIndex = i;
    for (int j = i + 1; j < n; ++j) {
        if (arr[j] < arr[minIndex])
            minIndex = j;
    }
    if (minIndex != i)
        swap(arr[i], arr[minIndex]);
}

正确答案正确

解析详情

【答案】正确

【考点】选择排序代码实现

【解析】 代码逻辑:外层循环遍历位置 i,内层循环寻找后续序列中最小元素的下标 minIndex,并在每轮结束时执行一次 swap 将最小值归位。这正是选择排序的标准流程,逻辑完全正确。

【易错点】 分不清“记录下标最后交换”(选择排序)与“相邻元素即时交换”(冒泡排序)的代码差异。

9 题(判断题

如果一个异常在 try 块中抛出但没有任何 catch 匹配,它将在编译时报错。

正确答案错误

解析详情

【答案】错误

【考点】异常捕获机制

【解析】 异常是在程序运行时抛出的。如果抛出的异常没有匹配的 catch 块,程序最终会调用 std::terminate() 强制终止。这是一个运行时崩溃行为,编译器通常无法在编译阶段检测出所有未捕获的路径。

【易错点】 混淆“编译时错误”(语法、类型不匹配)与“运行时错误/异常”(程序崩溃、逻辑失效)。

10 题(判断题

下面 C++ 代码实现将 Hello 写入 data.txt。

ofstream out("data.txt");
out << "Hello";
out.close();

正确答案正确

解析详情

【答案】正确

【考点】文件输出流 (ofstream)

【解析】 代码逻辑:通过 ofstream 对象创建并打开 data.txt。使用插入操作符 << 将字符串内容写入流,最后调用 close() 刷新缓冲区并释放文件资源。流程规范,能成功实现文件写入。

【易错点】 注意文件名路径权限等外部因素可能导致失败,但在语法和逻辑层面,这段代码是标准正确的文件操作范式。