- Admin 的博客
GESP C++ 4级重要知识点详解
- @ 2026-2-28 17:42:41
GESP C++ 四级核心知识点精讲与真题剖析
📋 目录
1. 📌 指针 (Pointers)
代码示例与说明:
#include <iostream>
using namespace std;
int main() {
int num = 42;
// 1. 定义与初始化:指针p存储变量num的地址
int *p = #
cout << "num的地址: " << p << endl; // 输出地址值
cout << "通过指针访问值: " << *p << endl; // 输出42,*为解引用运算符
// 2. 通过指针修改原变量
*p = 100;
cout << "修改后num的值: " << num << endl; // 输出100
// 3. 指针与数组的关系:数组名可视为指向首元素的指针常量
int arr[3] = {10, 20, 30};
int *ptr = arr; // 等价于 int *ptr = &arr[0];
cout << "arr[1] = " << arr[1] << endl;
cout << "*(ptr + 1) = " << *(ptr + 1) << endl; // 同样输出20
// 4. 指针常量与常量指针
const int *p1 = # // 指向常量的指针:不能通过p1修改num的值
// *p1 = 200; // 错误!
int *const p2 = # // 常量指针:p2的指向不能改变
// p2 = &arr[0]; // 错误!
return 0;
}
关键点:&取地址,*在声明中表示指针类型,在表达式中表示解引用。数组名是指向首元素的指针常量。
历年真题完整引用与解析:
-
2025年09月 第1题
第 1 题 运行下面程序后变量 a 的值是()。
int a = 10; int* p = &a; *p = 20;A. 10 B. 20 C. 编译错误 D. 不确定
解析:代码中
p指向a,*p = 20;即通过指针p解引用,修改了其指向的内存(变量a)的值为20。因此a的值变为20。 -
2025年09月 第2题
第 2 题 以下关于数组的描述中,()是错误的。 ☐ A. 数组名是一个指针常量
☐ B. 随机访问数组的元素方便快捷
☐ C. 数组可以像指针一样进行自增操作
☐ D. sizeof(arr) 返回的是整个数组 arr 占用的字节数解析:数组名是一个指向首元素的指针常量,其值(地址)不能改变,因此不能进行自增/自减操作(如
arr++)。选项C的描述是错误的。
易错点分析:
- 定义错误:
int *p = a;(错误,a不是地址)与int *p = &a;(正确)。 - 操作混淆:误以为可以对数组名进行自增操作(
arr++),实际上数组名是指针常量。 sizeof误解:混淆sizeof(arr)(返回整个数组字节数)与sizeof(p)(返回指针变量本身大小)。真题中明确指出sizeof(arr)返回整个数组占用的字节数是正确的描述。
2. 📊 二维及多维数组
代码示例与说明:
#include <iostream>
using namespace std;
// 正确传递二维数组:必须指定第二维(列)的大小
void printMatrix(int mat[][4], int rows) {
for(int i = 0; i < rows; i++) {
for(int j = 0; j < 4; j++) {
cout << mat[i][j] << " ";
}
cout << endl;
}
}
int main() {
// 1. 二维数组的初始化
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 2. 访问元素
cout << "matrix[1][2]: " << matrix[1][2] << endl; // 输出7
// 3. 理解内存连续性:按行优先连续存储
int *p = &matrix[0][0];
cout << "*(p + 4*1 + 2): " << *(p + 4*1 + 2) << endl; // 同样输出7,计算偏移
// 4. 将二维数组传递给函数
printMatrix(matrix, 3);
return 0;
}
关键点:内存按行连续存储。作为函数参数时,第一维大小可省略,第二维大小必须指定。
历年真题完整引用与解析:
-
2025年06月 第2题
第 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
解析:函数接收二维数组参数时,必须指定列数。
int** arr是指向指针的指针,其内存模型与二维数组不同,不能直接接收matrix这样的二维数组名。因此D选项是错误的。 -
2024年09月 第7题
第 7 题 在 C++ 中,()正确声明了一个 3 行 4 列的二维数组。 ☐ A. int arr[3, 4]; □ B. int arr[3][4]; □ C. int arr[4][3]; □ D. int arr(3, 4);
解析:C++中二维数组的正确声明方式是
type name[row][col];,因此int arr[3][4];是唯一正确的选项。
易错点分析:
- 函数参数声明错误:常见错误写法
int arr[][]或int **arr。正确写法为int arr[][4]或int (*arr)[4]。 - 初始化语法混淆:结构体和数组的初始化应使用花括号
{}。真题中明确指出Point p = {1,2};是正确的,而Point p = (1,2);是错误的。
3. 🏗️ 结构体 (Struct)
代码示例与说明:
#include <iostream>
#include <string>
#include <algorithm> // 使用sort函数需要包含此头文件
using namespace std;
// 1. 结构体定义
struct Student {
string name;
int age;
float score;
};
// 2. 结构体嵌套
struct Class {
string className;
Student monitor; // 嵌套Student结构体
};
// 3. 用于sort的比较函数:按成绩降序排序
bool cmpByScoreDesc(const Student &a, const Student &b) {
// 此处形参为什么用 const .. & ?
// & 引用传递,避免复制大结构体时的性能损失
// const 确保函数不会修改结构体内容
// 不考虑性能可以直接 Student a, Student b; 来定义每个参数
// 第一个参数a 代码前面 ,第二个参数b 代码后面
// 我们希望前面的分数大于后面的分数,才返回true
return a.score > b.score; // 降序
}
// 4. 用于sort的比较函数:先按年龄升序,年龄相同再按成绩降序
bool cmpComplex(const Student &a, const Student &b) {
// 年龄不同,按年龄升序
if (a.age != b.age)
return a.age < b.age; // 年龄升序
// 否则年龄相同,按成绩降序
else
return a.score > b.score; // 成绩降序
}
int main() {
// 5. 结构体变量的初始化(使用花括号{})
Student stu1 = {"张三", 15, 95.5};
Student stu2 = {"李四", 16, 90.0};
Student stu3 = {"王五", 15, 88.0};
Student stu4 = {"赵六", 16, 92.0};
// 6. 结构体数组
Student classA[4] = {stu1, stu2, stu3, stu4};
// 7. 使用自定义比较函数对结构体数组排序
// 7.1 按成绩降序排序
sort(classA, classA + 4, cmpByScoreDesc);
cout << "按成绩降序排序后:" << endl;
for (int i = 0; i < 4; i++) {
cout << classA[i].name << " " << classA[i].age << " " << classA[i].score << endl;
}
// 7.2 按复杂规则排序(先年龄升序,再成绩降序)
sort(classA, classA + 4, cmpComplex);
cout << "\n先年龄升序,再成绩降序排序后:" << endl;
for (int i = 0; i < 4; i++) {
cout << classA[i].name << " " << classA[i].age << " " << classA[i].score << endl;
}
// 8. 嵌套结构体的初始化和访问
Class myClass = {"四年级一班", stu1};
cout << myClass.className << "的班长是" << myClass.monitor.name << endl;
return 0;
}
关键点:使用struct关键字定义新类型,用.操作符访问成员,初始化必须使用{}。对结构体数组排序时,需要自定义比较函数来指定排序规则。
历年真题完整引用与解析:
-
2025年09月 第7题
第 7 题 关于结构体初始化,以下哪个选项中正确的是()。
1 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>;
解析:C++中结构体变量正确的初始化方式是使用花括号
{}进行列表初始化。因此选项BPoint p = {1,2};是唯一正确的。 -
2025年09月 第8题
第 8 题 运行如下代码会输出()。
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
解析:
birthday函数的参数是Cat&(引用传递),因此函数内c.age++直接修改了实参kitty的age成员。kitty.age初始为2,自增后变为3。输出为Mimi 3。
易错点分析:
- 初始化语法错误:误用圆括号
()、尖括号<>或new进行初始化。唯一正确语法是花括号{}。 - 成员访问混淆:通过结构体变量访问成员用
.,通过结构体指针访问成员用->。 - 结构体排序规则定义错误:对结构体数组使用
sort函数时,必须提供自定义比较函数。比较函数应返回bool值,表示第一个参数是否应排在第二个参数之前。定义多级排序规则时,逻辑要清晰。
4. 📏 函数与作用域
代码示例与说明:
#include <iostream>
using namespace std;
int globalVar = 100; // 全局变量
// 1. 函数声明(可指定默认参数)
int add(int a, int b = 5);
// 2. 参数传递方式对比
void byValue(int x) {
x += 10; // 只修改局部副本
}
void byReference(int &x) {
x += 10; // 修改原变量
}
void byPointer(int *x) {
if (x) *x += 10; // 通过指针修改原变量
}
int main() {
int localVar = 20;
// 3. 局部变量屏蔽全局变量
int globalVar = 50; // 局部变量,屏蔽了全局的globalVar
cout << "局部 globalVar: " << globalVar << endl; // 输出50
cout << "全局 globalVar: " << ::globalVar << endl; // 使用::访问全局,输出100
// 4. 测试参数传递
int num = 1;
byValue(num);
cout << "after byValue: " << num << endl; // 输出1,未改变
byReference(num);
cout << "after byReference: " << num << endl; // 输出11,已改变
byPointer(&num);
cout << "after byPointer: " << num << endl; // 输出21,已改变
// 5. 使用默认参数
cout << "add(3): " << add(3) << endl; // b使用默认值5,输出8
cout << "add(3, 4): " << add(3, 4) << endl; // b使用4,输出7
return 0;
}
// 函数定义
int add(int a, int b) {
return a + b;
}
关键点:局部作用域内定义的变量会屏蔽同名的全局变量。引用传递和指针传递可以修改实参并避免大对象拷贝。
历年真题完整引用与解析:
-
2025年09月 第5题
第 5 题 下面这段代码会输出()。
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
解析:
foo()函数内定义了局部变量x,其值为10,屏蔽了全局变量x,因此输出10。bar()函数内没有定义局部变量x,因此使用全局变量x,其值为5。最终输出10 5。 -
2025年03月 第5题
第 5 题 执行下述代码,将输出()。
void swap(int a, int &b) { int temp = a; a = b; b = temp; } int main() { int x = 1, y = 2; swap(x, y); std::cout << x << y; return 0; }□ A. 12 □ B. 21 □ C. 22 □ D. 11
解析:函数
swap的参数a是值传递,b是引用传递。调用swap(x, y)时,a是x的副本(值为1),b是y的引用。函数内交换了a和b的值,即交换了局部变量a和y。因此y被改为1,x不变。输出x和y为1和1。
易错点分析:
- 作用域混淆:认为局部变量修改后,全局变量也随之改变。实际上局部变量会屏蔽全局变量,要访问被屏蔽的全局变量需使用
::。 - 参数传递效果不清:误以为值传递可以修改实参。只有引用传递和指针传递才能修改实参。
- 默认参数位置:默认参数应在函数声明中指定,而非定义(除非声明与定义合一)。
5. 🧮 排序算法
代码示例与说明:
#include <iostream>
#include <vector>
using namespace std;
// 1. 选择排序 (不稳定)
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 (nums[j] < nums[minIndex]) { // 寻找最小值
minIndex = j;
}
}
// 将找到的最小值与当前位置i交换
if (minIndex != i) {
swap(nums[i], nums[minIndex]);
}
}
}
// 2. 冒泡排序 (稳定,可优化)
void bubbleSort(vector<int>& nums) {
int n = nums.size();
for (int i = 0; i < n - 1; ++i) {
bool swapped = false; // 优化标志
for (int j = 0; j < n - i - 1; ++j) {
if (nums[j] > nums[j + 1]) { // 相邻元素比较
swap(nums[j], nums[j + 1]);
swapped = true;
}
}
if (!swapped) break; // 本轮无交换,说明已有序,提前结束
}
}
// 3. 插入排序 (稳定)
void insertionSort(vector<int>& nums) {
int n = nums.size();
for (int i = 1; i < n; ++i) { // 从第二个元素开始
int key = nums[i]; // 待插入的元素
int j = i - 1;
// 将大于key的元素向后移动
while (j >= 0 && nums[j] > key) {
nums[j + 1] = nums[j];
j--;
}
nums[j + 1] = key; // 插入key到正确位置
}
}
关键点:掌握三种排序的双层循环结构。选择排序不稳定,冒泡和插入稳定。插入排序在最好情况(已有序)下时间复杂度为O(n)。
历年真题完整引用与解析:
-
2025年09月 第9题
第9题 关于排序算法的稳定性,以下说法错误的是()。 A. 稳定的排序算法不改变相等元素的相对位置
B. 冒泡排序是稳定的排序算法
C. 选择排序是稳定的排序算法
D. 插入排序是稳定的排序算法解析:稳定性是指排序后相等元素的相对顺序保持不变。冒泡排序和插入排序是稳定的。选择排序在交换元素时可能改变相等元素的原始顺序,因此是不稳定的。选项C的说法错误。
-
2025年09月 第10题
第 10 题 下面代码试图实现选择排序,使其能对数组 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. 1 nums[j] < nums[minIndex] 2 swap(nums[i], nums[minIndex])
□ B
1 nums[j] > nums[minIndex] 2 swap(nums[i], nums[minIndex])
□ C
1 nums[j] <= nums[minIndex] 2 swap(nums[j], nums[minIndex])
D
1 nums[j] <= nums[minIndex] 2 swap(nums[i], nums[j])
解析:选择排序升序排序时,内循环应寻找最小元素的下标。因此比较条件应为
nums[j] < nums[minIndex]。找到最小元素下标minIndex后,应将其与当前位置i的元素交换。因此正确答案是A。 -
2025年09月 第11题
第 11 题 下面程序实现插入排序(升序排序),则横线上应分别填写()。
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
1 arr[j] > key 2 arr[j + 1] = key
B
1 arr[j] < key 2 arr[j + 1] = key
□ C
1 arr[j] > key 2 arr[j] = key
D
1 arr[j] < key 2 arr[j] = key
解析:插入排序升序排序时,内循环
while的任务是将所有大于key的元素向后移动一位。因此条件应为arr[j] > key。循环结束后,j+1的位置就是key应该插入的位置。因此正确答案是A。
易错点分析:
- 稳定性判断错误:最常考且最易错的是认为选择排序是稳定的。必须牢记:选择排序不稳定,冒泡和插入稳定。
- 代码细节混淆:
- 选择排序内循环找的是最小(升序)或最大(降序)元素的下标,而不是值。
- 插入排序内循环的移动条件是
arr[j] > key(升序),且循环结束后插入位置是j+1。 - 冒泡排序的优化标志位
swapped用于提前终止。
- 时间复杂度记错:插入排序在最好情况(已有序)下时间复杂度是O(n),而非总是O(n²)。
6. 🔄 递推算法
代码示例与说明:
#include <iostream>
using namespace std;
// 经典问题:爬楼梯,每次可以爬1阶或2阶,求到第n阶的方法数
// 递推关系:f(n) = f(n-1) + f(n-2), 其中 f(1)=1, f(2)=2
int climbStairs(int n) {
if (n <= 2) return n; // 边界条件
// 使用三个变量滚动计算,避免使用数组
int prev2 = 1; // f(i-2),初始为f(1)
int prev1 = 2; // f(i-1),初始为f(2)
int current = 0;
for (int i = 3; i <= n; ++i) {
current = prev1 + prev2; // 计算 f(i)
// 更新状态,为下一次迭代做准备
prev2 = prev1;
prev1 = current;
}
return current;
}
// 斐波那契数列:f(n) = f(n-1) + f(n-2), f(0)=0, f(1)=1
int fibonacci(int n) {
if (n <= 1) return n;
int a = 0, b = 1, c;
for (int i = 2; i <= n; ++i) {
c = a + b;
a = b;
b = c;
}
return b;
}
int main() {
cout << "爬到第5阶有 " << climbStairs(5) << " 种方法" << endl; // 输出8
cout << "斐波那契第6项是 " << fibonacci(6) << endl; // 输出8
return 0;
}
关键点:确定边界条件和递推关系式。使用循环和少量变量(滚动数组)迭代计算,避免递归的低效。
历年真题完整引用与解析:
-
2025年09月 第13题
第 13 题 小杨正在爬楼梯,需要 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; prev1 = current; prev2 = prev1; current = prev1 + prev2;
解析:递推关系为
current = prev1 + prev2。计算完新的current后,需要更新prev2和prev1为下一次迭代做准备,正确的更新顺序是:先将prev1的值赋给prev2,再将current的值赋给prev1。因此选项Bcurrent = prev1 + prev2; prev2 = prev1; prev1 = current;是正确的。
易错点分析:
- 状态更新顺序错误:如选项A,先更新
prev2和prev1,再计算current,这会导致current使用了错误的前状态值。 - 边界条件遗漏:未处理
n <= 2的情况直接进入循环,会导致数组越界或逻辑错误。 - 与递归混淆:递推是正向迭代,递归是函数自调用。递推效率更高,是考题首选实现方式。
7. 📁 文件操作
代码示例与说明:
#include <iostream>
#include <fstream>
#include <string>
using namespace std;
// 方法1:使用文件流对象 (最清晰、最常用)
void method1_file_stream() {
cout << "=== 方法1:使用文件流对象 ===" << endl;
// 写入文件
ofstream outFile("data1.txt");
if (outFile.is_open()) {
outFile << "Hello, GESP!" << endl;
outFile << 100 << " " << 200 << endl;
outFile.close();
cout << "文件写入成功" << endl;
} else {
cout << "文件打开失败" << endl;
}
// 读取文件
ifstream inFile("data1.txt");
string line;
int a, b;
if (inFile.is_open()) {
getline(inFile, line);
cout << "第一行: " << line << endl;
inFile >> a >> b;
cout << "两个数: " << a << ", " << b << endl;
inFile.close();
} else {
cout << "文件打开失败" << endl;
}
cout << endl;
}
// 方法2:使用freopen重定向标准输出
void method2_freopen() {
cout << "=== 方法2:使用freopen重定向标准输出 ===" << endl;
// 重定向cout到文件
freopen("output.txt", "w", stdout);
cout << "This goes to file." << endl;
cout << "Another line." << endl;
// 恢复标准输出(部分环境需要)
fclose(stdout);
// 重新打开标准输出到控制台
freopen("CON", "w", stdout);
cout << "Back to console" << endl;
cout << "文件写入成功" << endl;
cout << endl;
}
// 方法3:使用rdbuf重定向(较高级)
void method3_rdbuf() {
cout << "=== 方法3:使用rdbuf重定向 ===" << endl;
ofstream outFile("data2.txt");
streambuf *oldBuf = cout.rdbuf(); // 保存旧缓冲区
// 重定向cout到文件
cout.rdbuf(outFile.rdbuf());
cout << "Redirected by rdbuf" << endl;
cout << "Another line using rdbuf" << endl;
// 恢复cout
cout.rdbuf(oldBuf);
cout << "Back to console" << endl;
cout << "文件写入成功" << endl;
outFile.close();
cout << endl;
}
// 方法4:文件读取的不同方式
void method4_file_reading() {
cout << "=== 方法4:文件读取的不同方式 ===" << endl;
// 准备测试文件
ofstream outFile("test_read.txt");
outFile << "Line 1: Hello" << endl;
outFile << "Line 2: 123 456" << endl;
outFile << "Line 3: Test" << endl;
outFile.close();
// 方式1:逐行读取
ifstream inFile1("test_read.txt");
string line;
cout << "逐行读取:" << endl;
while (getline(inFile1, line)) {
cout << "" << line << endl;
}
inFile1.close();
// 方式2:按空格分隔读取
ifstream inFile2("test_read.txt");
string word;
cout << "\n按空格分隔读取:" << endl;
while (inFile2 >> word) {
cout << "" << word << endl;
}
inFile2.close();
cout << endl;
}
int main() {
method1_file_stream();
method2_freopen();
method3_rdbuf();
method4_file_reading();
cout << "所有文件操作方法演示完成!" << endl;
return 0;
}
关键点:ofstream用于写,ifstream用于读。freopen可以重定向cin/cout。核心是区分数据写入的目标是文件流对象还是标准流。
历年真题完整引用与解析:
-
2025年03月 第15题
第 15 题 下面哪种方式不能实现将字符串 "Happy Spring!" 输出重定向到文件 log.txt()。
□ A
1 freopen("log.txt", "w", stdout); 2 cout << "Happy Spring!" << endl; 3 fclose(stdout);
□ B
std::ofstream outFile("log.txt"); outFile << "Happy Spring!" << endl; outFile.close();
□ C
std::ofstream outFile("log.txt"); cout << "Happy Spring!" << endl; outFile.close();
□ D
1 ofstream log_file("log.txt"); 2 streambuf* org_cout = cout.rdbuf(); 3 cout.rdbuf(log_file.rdbuf()); 4 cout << "Happy Spring!" << endl; 5 cout.rdbuf(org_cout);
解析:选项C中,虽然创建了文件流对象
outFile,但输出语句cout << "Happy Spring!" << endl;仍然将内容输出到了标准控制台(cout),而不是文件log.txt。因此选项C不能实现重定向到文件的功能。
易错点分析:
- 目标混淆:定义了文件流对象(如
outFile),却错误地使用cout进行输出,导致内容输出到屏幕而非文件。 - 文件未检查是否打开:直接对文件流进行读写操作,如果文件打开失败可能导致程序问题。良好的习惯是使用
is_open()检查。 - 重定向后未恢复:使用
freopen或rdbuf重定向标准流后,在某些情况下需要恢复,否则后续的所有输出都会受到影响。
8. 🚨 异常处理
代码示例与说明:
#include <iostream>
#include <stdexcept> // 包含标准异常类
using namespace std;
double safeDivide(int numerator, int denominator) {
if (denominator == 0) {
// 抛出异常,可以使用标准异常或自定义字符串
throw runtime_error("除数不能为零!");
// 也可以抛出其他类型: throw "Division by zero";
}
return static_cast<double>(numerator) / denominator;
}
int main() {
int a = 10, b = 0;
try {
// try块包含可能抛出异常的代码
double result = safeDivide(a, b);
cout << "结果是: " << result << endl;
// 如果上一句抛出异常,则try块内后面的代码不会执行
cout << "这行不会被执行" << endl;
}
// catch块按顺序匹配异常类型
catch (const runtime_error& e) { // 捕获runtime_error类型异常
cout << "捕获到标准异常: " << e.what() << endl; // e.what()返回错误信息
}
catch (const char* msg) { // 捕获字符串字面量类型的异常
cout << "捕获到字符串异常: " << msg << endl;
}
catch (...) { // 捕获所有其他类型的异常
cout << "捕获到未知类型异常" << endl;
}
// 异常被处理后,程序继续执行
cout << "程序继续运行..." << endl;
// 示例:未捕获的异常
// throw 100; // 如果取消注释,此异常未被捕获,程序会调用terminate终止
return 0;
}
关键点:try块尝试执行可能出错的代码;throw抛出异常;catch捕获并处理特定类型的异常。未被捕获的异常会导致程序终止。
历年真题完整引用与解析:
-
2025年09月 第15题
第 15 题 关于异常处理,以下说法错误的是()。 ☐ C. throw 语句用于抛出异常 ☐ D. 所有异常都必须被捕获,否则程序会崩溃
解析:
throw语句确实用于抛出异常,说法C正确。说法D“所有异常都必须被捕获”是错误的。C++中,如果异常在try块中抛出但没有匹配的catch块捕获它,该异常将传播到上一层调用者。如果在整个调用链中都未被捕获,程序将调用std::terminate()函数终止,这通常表现为程序崩溃,但“必须被捕获”不是语法强制要求,因此说法不准确。 -
2024年12月 第15题
第 15 题 运行下面的代码,将出现什么情况?()
double hmean(double a, double b) { if (a == -b) throw runtime_error("Runtime error occurred"); } return 2.0 * a * b / (a + b); int main() { double x = 10; double y = -10; try { int result = hmean(x, y); cout << "hmean: " << result << endl; } catch (const runtime_error& e) { cout << "Caught: " << e.what() << endl; } catch (...) { cout << "Caught an unknown exception." << endl; } return 0; }□ A. 屏幕上输出 Caught: Runtime error occurred □ B. 屏幕上输出 Caught an unknown exception □ C. 程序调用 std::terminate() □ D. 编译错误
解析:调用
hmean(10, -10)时,满足a == -b条件,函数抛出runtime_error异常。在main的try块中,此异常被后面的catch (const runtime_error& e)子句成功捕获,因此会执行该catch块中的代码,输出Caught: Runtime error occurred。程序不会崩溃、不会调用terminate,也没有编译错误。
易错点分析:
- 对“必须捕获”的误解:认为所有可能抛出的异常都必须在代码中用
catch捕获,否则无法通过编译。实际上,语法上并不强制,但未捕获的异常会导致运行时程序终止。 - 执行流程不清:误认为
try块中抛出异常后,其后的代码仍会执行。实际上,控制流会立即跳转到匹配的catch块。 - catch块顺序:
catch (...)(捕获所有异常)应该放在所有具体类型catch块的后面,否则它会拦截所有异常,使后面的具体catch块失效。