GESP 客观题评测系统

试卷解析 · 理解每一步

2025-09-Level-4

2025-09-Level-4

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

单选题

第 1 题(单选题,2 分)

运行下面程序后变量 a 的值是()。

int a = 42;
int* p = &a;
*p = *p + 1;
A.
42
B.
43
C.
编译错误
D.
不确定

正确答案B

解析详情
答案 B指针解引用与变量修改
思路与推导
  1. 1. 指针 p 存储了变量 a 的地址(&a)。

  2. 2. *p = *p + 1 通过解引用访问 a 的值(42),加 1 后赋值回 a。

  3. 3. 最终 a 的值更新为 43。

第 2 题(单选题,2 分)

以下关于数组的描述中,()是错误的。

A.
数组名是一个指针常量
B.
随机访问数组的元素方便快捷
C.
数组可以像指针一样进行自增操作
D.
sizeof(arr) 返回的是整个数组 arr 占用的字节数

正确答案C

解析详情
答案 C数组与指针的区别
思路与推导
  1. 在 C++ 中,数组名被视为常量指针,其指向的内存地址在定义后不可更改。

  2. 因此,不能对数组名执行自增(arr++)或自减(arr--)等修改指针指向的操作。

  3. 其他选项如随机访问和 sizeof 运算均符合数组的基本特性。

第 3 题(单选题,2 分)

给定如下定义的数组 arr,则∗(∗(arr+1)+2)*(*(\text{arr} + 1) + 2)的值是()。

int arr[2][3] = {{1, 2, 3}, {4, 5, 6}};
A.
2
B.
5
C.
4
D.
6

正确答案D

解析详情
答案 D二维数组的指针表示法
思路与推导
  1. 1. arr + 1 定位到二维数组的第二行(下标为 1 的行)。

  2. 2. *(arr + 1) 获取该行的首地址,+(2) 偏移到该行的第三个元素。

  3. 3. 最外层的 * 获取该位置的值,即 arr[1][2] 的值 6。

第 4 题(单选题,2 分)

下面这段代码会输出()。

int add(int a, int b = 1); // 函数声明

int main() {
    cout << add(2) << " " << add(2, 3);
    return 0;
}

int add(int a, int b) { // 函数定义
    return a + b;
}
A.
3 5
B.
编译失败:定义处少了默认参数
C.
运行错误
D.
链接失败:未定义引用

正确答案A

解析详情
答案 A默认参数及其应用
思路与推导
  1. 1. add(2) 调用时使用默认参数 b=1,返回 2+1=3。

  2. 2. add(2, 3) 调用时显式提供参数 b=3,覆盖默认值,返回 2+3=5。

  3. 3. C++ 允许在声明中指定默认值,定义时无需重复。

第 5 题(单选题,2 分)

下面这段代码会输出()。

int x = 5;

void foo() {
    int x = 10;
    cout << x << " ";
}

void bar() {
    cout << x << " ";
}

int main() {
    foo();
    bar();
}
A.
5 5
B.
10 10
C.
5 10
D.
10 5

正确答案D

解析详情
答案 D局部变量作用域屏蔽
思路与推导
  1. 1. foo() 内部定义了局部变量 x=10,它屏蔽了全局变量 x,故输出 10。

  2. 2. bar() 内部没有局部变量 x,直接访问全局变量 x=5,故输出 5。

  3. 3. 局部变量的声明仅在所属函数作用域内有效,不改变全局变量。

第 6 题(单选题,2 分)

下面程序运行的结果是()。

void increaseA(int x) {
    x++;
}

void increaseB(int* p) {
    (*p)++;
}

int main() {
    int a = 5;
    increaseA(a);
    cout << a << " ";
    increaseB(&a);
    cout << a;
}
A.
6 7
B.
6 6
C.
5 6
D.
5 5

正确答案C

解析详情
答案 C值传递与指针传递
思路与推导
  1. 1. increaseA(a) 是值传递,操作的是副本,a 的值保持 5。

  2. 2. increaseB(&a) 是指针传递,通过解引用直接修改了 main 中的 a。

  3. 3. 因此 a 的值自增为 6,两次输出分别为 5 和 6。

第 7 题(单选题,2 分)

关于结构体初始化,以下哪个选项中正确的是()。

struct Point { int x, y; };
A.
Point p = (1,2);
B.
Point p = {1,2};
C.
Point p = new {1,2};
D.
Point p = <1,2>;

正确答案B

解析详情
答案 B结构体初始化语法
思路与推导
  1. C++ 中结构体是聚合类型,标准初始化方式是使用大括号 {} 列表。

  2. Point p = {1, 2}; 按顺序为结构体成员 x 和 y 赋初值。

  3. 其他符号如 ()、new {}、<> 均不符合 C++ 结构体栈上初始化的语法。

第 8 题(单选题,2 分)

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

struct Cat {
    string name;
    int age;
};

void birthday(Cat& c) {
    c.age++;
}

int main() {
    Cat kitty{"Mimi", 2};
    birthday(kitty);
    cout << kitty.name << " " << kitty.age;
}
A.
Mimi 2
B.
Mimi 3
C.
kitty 3
D.
kitty 2

正确答案B

解析详情
答案 B结构体引用传递
思路与推导
  1. 1. birthday 函数接收 Cat 对象的引用,c.age++ 直接修改实参。

  2. 2. kitty 被初始化为 {"Mimi", 2},调用函数后其 age 变为 3。

  3. 3. 输出 kitty.name ("Mimi") 和更新后的 age (3)。

第 9 题(单选题,2 分)

关于排序算法的稳定性,以下说法错误的是()。

A.
稳定的排序算法不改变相等元素的相对位置
B.
冒泡排序是稳定的排序算法
C.
选择排序是稳定的排序算法
D.
插入排序是稳定的排序算法

正确答案C

解析详情
答案 C排序算法稳定性
思路与推导
  1. 稳定性指相等元素排序前后的相对位置是否保持不变。

  2. 选择排序在每轮寻找最小值并交换到首位时,可能发生跨越式交换。

  3. 这种交换极易破坏相等元素的原始顺序,因此是不稳定的排序算法。

第 10 题(单选题,2 分)

下面代码试图实现选择排序,使其能对数组 nums 排序为升序,则横线上应分别填写()。

void selectionSort(vector<int>& nums) {
    int n = nums.size();
    for (int i = 0; i < n - 1; ++i) {
        int minIndex = i;
        for (int j = i + 1; j < n; ++j) {
            if (___) { // 在此处填入代码
                minIndex = j;
            }
        }
    }
    // 在此处填入代码
}
A.
nums[j] < nums[minIndex]
swap(nums[i], nums[minIndex])
B.
nums[j] > nums[minIndex]
swap(nums[i], nums[minIndex])
C.
nums[j] <= nums[minIndex]
swap(nums[j], nums[minIndex])
D.
nums[j] <= nums[minIndex]
swap(nums[i], nums[j])

正确答案A

解析详情
答案 A选择排序实现逻辑
思路与推导
  1. 1. 升序选择排序需在未排序部分寻找最小值,判断条件应为 nums[j] < nums[minIndex]。

  2. 2. 找到本轮最小值下标后,将其与当前起始位置 i 进行交换。

  3. 3. 对应的代码为 swap(nums[i], nums[minIndex])。

第 11 题(单选题,2 分)

下面程序实现插入排序(升序排序),则横线上应分别填写()。

void insertionSort(int arr[], int n) {
    for (int i = 1; i < n; i++) {
        int key = arr[i];
        int j = i - 1;
        while (j >= 0 && _____) { // 在此处填入代码
            arr[j + 1] = arr[j];
            j--;
        }
        ___; // 在此处填入代码
    }
}
A.
arr[j] > key
arr[j + 1] = key
B.
arr[j] < key
arr[j + 1] = key
C.
arr[j] > key
arr[j] = key
D.
arr[j] < key
arr[j] = key

正确答案A

解析详情
答案 A插入排序实现逻辑
思路与推导
  1. 1. 插入排序需将待插入值 key 与前面已排序元素比较,若 arr[j] > key 则后移。

  2. 2. 循环结束后,j 指向第一个不大于 key 的元素位置。

  3. 3. 最终将 key 插入到其后的空位,即 arr[j + 1] = key。

第 12 题(单选题,2 分)

关于插入排序的时间复杂度,下列说法正确的是()。

A.
最好情况和最坏情况的时间复杂度都是O(n2)O(n^{2})
B.
最好情况是O(n)O(n),最坏情况是O(n2)O(n^{2})
C.
最好情况是O(n)O(n),最坏情况是O(2n)O(2^{n})
D.
最好情况是O(n2)O(n^{2}),最坏情况是O(2n)O(2^{n})

正确答案B

解析详情
答案 B插入排序时间复杂度
思路与推导
  1. 1. 最好情况:数组已升序,每轮只需一次比较,总时间复杂度为 O(n)。

  2. 2. 最坏情况:数组全逆序,每个元素都要移动到最前,复杂度为 O(n²)。

  3. 3. 平均情况下,插入排序的时间复杂度也为 O(n²)。

第 13 题(单选题,2 分)

小杨正在爬楼梯,需要 n 阶才能到达楼顶,每次可以爬 1 阶或 2 阶,求小杨有多少种不同的方法可以爬到楼顶,横线上应填写()。

int climbStairs(int n) {
    if (n <= 2) return n;
    int prev2 = 1;
    int prev1 = 2;
    int current = 0;
    for (int i = 3; i <= n; ++i) {
        // 在此处填入代码
    }
    return current;
}
A.
prev2 = prev1;
prev1 = current;
current = prev1 + prev2;
B.
current = prev1 + prev2;
prev2 = prev1;
prev1 = current;
C.
current = prev1 + prev2;
prev1 = current;
prev2 = prev1;
D.
prev1 = current;
prev2 = prev1;
current = prev1 + prev2;

正确答案B

解析详情
答案 B爬楼梯递推模型
思路与推导
  1. 1. 核心递推式:current = prev1 + prev2,代表当前阶法等于前两阶法之和。

  2. 2. 计算出 current 后,需更新状态:旧的 prev1 变为新的 prev2,current 变为新的 prev1。

  3. 3. 更新顺序必须先计算 current,再依次移动状态窗口,否则会丢失旧值。

第 14 题(单选题,2 分)

假设有一个班级的成绩单,存储在一个长度为 n 的数组 scores 中,每个元素是一个学生的分数。老师想要找出 所有满足 scores[i] + scores[j] + scores[k] == 300 的三元组,其中 i < j < k。下面代码实现该功能,请问其时间复杂度是( )。

int cnt = 0;
for (int i = 0; i < n; i++) {
    for (int j = i + 1; j < n; j++) {
        for (int k = j + 1; k < n; k++) {
            if (scores[i] + scores[j] + scores[k] == 300) {
                cnt++;
            }
        }
    }
}
A.
O(n)O(n)
B.
O(n2)O(n^2)
C.
O(n3)O(n^3)
D.
O(2n)O(2^n)

正确答案C

解析详情
答案 C时间复杂度分析
思路与推导
  1. 1. 代码包含三层嵌套循环,每层循环的上限均与 n 线性相关。

  2. 2. 总执行次数约为 n * (n-1) * (n-2) / 6,主项为 n³。

  3. 3. 因此该算法在大 O 表示法下对应的复杂度为 O(n³)。

第 15 题(单选题,2 分)

关于异常处理,以下说法错误的是()。

A.
try 块中的代码可能会抛出异常
B.
catch 块可以有多个,处理不同类型的异常
C.
throw 语句用于抛出异常
D.
所有异常都必须被捕获,否则程序会崩溃

正确答案D

解析详情
答案 D异常处理机制
思路与推导
  1. C++ 并不强制捕获所有可能抛出的异常,异常可以沿着调用栈向上层传递。

  2. 未捕获的异常最终会导致程序调用 std::terminate 并崩溃。

  3. 但“必须捕获”的说法过于绝对,开发者可以选择在最合适的层次处理。

判断题

第 1 题(判断题,2 分)

以下代码能正确初始化指针。

int a = 5;
int *p = a;

正确答案错误

解析详情
答案 错误指针初始化规范
思路与推导
  1. 指针变量必须存储内存地址,通常使用取地址符 & 获取变量地址。

  2. int *p = a; 是将整数 5 错误地当作地址赋给指针,会导致语法错误。

  3. 正确做法应为 int *p = &a; 使 p 指向变量 a。

第 2 题(判断题,2 分)

执行下面C++代码将输出11。

int x = 10;
void f() {
    int x = x + 1;
    cout << x << endl;
}

int main() {
    f();
}

正确答案错误

解析详情
答案 错误局部变量屏蔽与初始化
思路与推导
  1. 函数 f 内声明了局部变量 int x,这会屏蔽同名的全局变量。

  2. 语句 int x = x + 1; 中的右值 x 引用的是尚未初始化的局部变量。

  3. 使用未初始化的变量进行加法运算会产生随机结果,而非 11。

第 3 题(判断题,2 分)

以下C++代码合法。

struct Student {
    string name;
    int age;
    float score;
};
Student* students = new Student[20];

正确答案正确

解析详情
答案 正确结构体动态内存分配
思路与推导
  1. Student 结构体定义正确。new Student[20] 是合法的语法。

  2. 它在堆上申请了 20 个 Student 对象大小的连续内存,并返回首地址。

  3. 指针变量 students 接收该地址,用于后续访问该数组。

第 4 题(判断题,2 分)

执行下面C++代码将输出10。

void func(int* p) {
    *p = 10;
}

int main() {
    int a = 5;
    func(&a);
    cout << a << endl;
    return 0;
}

正确答案正确

解析详情
答案 正确指针传递修改实参
思路与推导
  1. func(&a) 将变量 a 的地址传递给函数。

  2. 函数内部通过解引用 *p = 10 直接操作该地址对应的内存单元。

  3. 因此 main 中的 a 被成功修改为 10,输出正确。

第 5 题(判断题,2 分)

下面代码将二维数组 arr 传递给函数 f,函数内部用 arr[i][j] 访问元素,函数参数声明为 int arr[][4] 是错误的。

void f(int arr[][4], int rows) {
    // 访问 arr[i][j]
}

int main() {
    int arr[3][4] = { /* 初始化 */ };
    f(arr, 3);
}

正确答案错误

解析详情
答案 错误二维数组传参规则
思路与推导
  1. C++ 二维数组传参时,第一维大小可省略,但第二维(列宽)必须明确。

  2. 编译器需要列宽来计算 arr[i][j] 的内存偏移量。

  3. 因此 int arr[][4] 是标准且正确的写法,题干描述为错误是错误的。

第 6 题(判断题,2 分)

递推是在给定初始条件下,已知前一项(或前几项)求后一项的过程。

正确答案正确

解析详情
答案 正确递推算法定义
思路与推导
  1. 递推是典型的自底向上算法,从已知的基础状态(初始条件)出发。

  2. 利用明确的数学关系(递推公式)逐步求解更大规模的子问题。

  3. 这正是递推算法的核心定义和执行过程。

第 7 题(判断题,2 分)

虽然插入排序的时间复杂度为O(n2)O\left(n^{2}\right),但由于单元操作相对较少,因此在小数据量的排序任务中非常受欢迎。

正确答案正确

解析详情
答案 正确插入排序应用场景
思路与推导
  1. 插入排序虽为 O(n²),但在小规模或基本有序的数据下表现优异。

  2. 它的常数项系数极小,且无需额外的辅助内存空间。

  3. 许多标准库排序算法在最后阶段会切换到插入排序以提升性能。

第 8 题(判断题,2 分)

对整数数组{4,1,3,1,5,2}\{4, 1, 3, 1, 5, 2\}进行冒泡排序(将最大元素放到最后),执行一轮之后是{4,1,3,1,2,5}\{4, 1, 3, 1, 2, 5\}。

正确答案错误

解析详情
答案 错误冒泡排序执行过程
思路与推导
  1. 第一轮冒泡应将序列中最大的元素(5)交换到最后一位。

  2. 初始序列末尾是 2,经过一轮完整的比较与交换后,5 应该在末位。

  3. 题干给出的结果序列中,最大值 5 已定位到最后,但序列前端 1 仍在首位是不对的。

第 9 题(判断题,2 分)

以下代码只能捕获 int 类型异常。

int main() {
    try {
        throw 42;
    } catch (...) {
        cout << "Caught" << endl;
    }
    return 0;
}

正确答案错误

解析详情
答案 错误C++ 万能捕获语法
思路与推导
  1. catch (...) 是 C++ 中的万能捕获语法。

  2. 它可以捕获 try 块中抛出的任何类型的异常,不仅限于 int。

  3. 题干中称其“只能捕获 int”显然是错误的描述。

第 10 题(判断题,2 分)

以下代码将 Hello 写入文件 data.txt。

ofstream file("data.txt");
cout << "Hello" << endl;
file.close();

正确答案错误

解析详情
答案 错误文件流与标准输出
思路与推导
  1. 代码中虽然定义了文件流 file,但输出使用的是 cout << "Hello"。

  2. cout 会将内容输出到屏幕标准输出,而不是写入 data.txt 文件。

  3. 若要写入文件,应使用 file << "Hello" 语句。