GESP C++ 二级知识点的超详细解析

发布于:2025-08-05 ⋅ 阅读:(15) ⋅ 点赞:(0)

以下是 GESP C++ 二级知识点的超详细解析,结合语法细节、典型例题、易错点及考试重点,帮助你彻底掌握核心内容。


一、C++ 语法进阶

1. 控制结构(复杂应用)

(1)循环嵌套

核心:外层循环控制“行”,内层循环控制“列”,适用于二维问题(如矩阵、图形打印)。
例题:打印杨辉三角前5行
杨辉三角规律:第 i 行(从0开始)有 i+1 个数,首尾为1,中间数等于上一行左右两数之和(a[i][j] = a[i-1][j-1] + a[i-1][j])。

#include <iostream>
using namespace std;

int main() {
    int row = 5;
    int a[row][row]; // 定义二维数组存储杨辉三角

    for (int i = 0; i < row; i++) {       // 外层循环:控制行
        for (int j = 0; j <= i; j++) {    // 内层循环:控制列(第i行有i+1列)
            if (j == 0 || j == i) {       // 首尾元素为1
                a[i][j] = 1;
            } else {                      // 中间元素由上一行计算
                a[i][j] = a[i-1][j-1] + a[i-1][j];
            }
            cout << a[i][j] << " ";
        }
        cout << endl;  // 每行结束换行
    }
    return 0;
}

输出结果

1 
1 1 
1 2 1 
1 3 3 1 
1 4 6 4 1 

易错点

  • 二维数组索引从0开始,内层循环的终止条件应为 j <= i(而非 j < row)。
  • 计算 a[i][j] 时,需确保 i-1 >= 0j-1 >= 0(首尾元素直接赋值1可避免越界)。
(2)条件分支扩展

switch-case:适用于多分支选择,case 后必须为常量,且需 break 终止(否则会穿透)。
例题:成绩等级评定(输入分数,输出A/B/C/D)

#include <iostream>
using namespace std;

int main() {
    int score;
    cin >> score;
    switch (score / 10) {  // 分数除以10取整,得到十位数字
        case 10:           // 100分
        case 9:  cout << "A" << endl; break;
        case 8:  cout << "B" << endl; break;
        case 7:  cout << "C" << endl; break;
        case 6:  cout << "D" << endl; break;
        default: cout << "E" << endl;  // 60分以下
    }
    return 0;
}

注意:若 score 为100,score/10 结果为10,匹配 case 10,但因无 break 会继续执行后续 case(此处 case 10 后无代码,最终输出A)。

(3)跳转语句
  • break:终止当前所在的最内层循环(或 switch 语句)。
  • continue:跳过当前循环的剩余代码,直接进入下一次循环。
  • goto:跳转到指定标签(label:),但易导致代码混乱,考试中不建议使用(仅在深层嵌套跳出时偶尔用)。

2. 数组(核心重点)

(1)一维数组

定义与初始化

  • 完全初始化:int a[5] = {1,2,3,4,5};(元素个数与声明一致)。
  • 部分初始化:int a[5] = {1,2};(未初始化元素自动为0)。
  • 省略长度:int a[] = {1,2,3};(长度由初始化列表自动确定)。

遍历数组

int a[5] = {3,1,4,2,5};
// 正向遍历
for (int i = 0; i < 5; i++) {
    cout << a[i] << " ";
}
// 反向遍历
for (int i = 4; i >= 0; i--) {
    cout << a[i] << " ";
}

数组操作

  • 求最值:遍历数组,记录当前最大/最小值。
    int max_val = a[0];
    for (int i = 1; i < 5; i++) {
        if (a[i] > max_val) max_val = a[i];
    }
    
  • 求和/统计频次:累加或计数符合条件的元素。
    int sum = 0;
    int count = 0;
    for (int i = 0; i < 5; i++) {
        sum += a[i];
        if (a[i] % 2 == 0) count++;  // 统计偶数个数
    }
    
  • 删除元素:需将后续元素前移,最后长度减1(数组长度固定,无法真正删除)。
    int a[5] = {1,2,3,4,5};
    int pos = 2;  // 删除索引2的元素(值为3)
    for (int i = pos; i < 4; i++) {  // 最后一个元素无需移动
        a[i] = a[i+1];
    }
    // 此时数组变为 [1,2,4,5,5],有效长度为4
    

易错点

  • 数组索引从0开始,最大索引为 长度-1(如 a[5] 的索引是0~4,访问 a[5] 越界)。
  • 部分初始化时,未显式赋值的元素为0(如 int a[3] = {1};a[0]=1, a[1]=0, a[2]=0)。
(2)二维数组

定义与内存存储

  • 定义:int b[3][4]; 表示3行4列的数组(共12个元素)。
  • 内存存储:按行连续存储(b[0][0], b[0][1], ..., b[0][3], b[1][0], ...)。

输入输出

int b[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
// 输出所有元素(按行)
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 4; j++) {
        cout << b[i][j] << "\t";  // \t 制表符对齐
    }
    cout << endl;
}

典型应用

  • 矩阵转置:行列互换(b[i][j]b[j][i] 交换)。
    int b[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
    int trans[3][3];
    for (int i = 0; i < 3; i++) {
        for (int j = 0; j < 3; j++) {
            trans[j][i] = b[i][j];  // 行变列,列变行
        }
    }
    
  • 方阵旋转:顺时针旋转90度(先转置,再反转每行)。

易错点

  • 二维数组定义时,第二维长度必须指定(如 int b[][4] = {{1,2},{3}}; 合法,第一维可省略)。
  • 访问 b[i][j] 时,i 范围是 0~行数-1j 范围是 0~列数-1(越界会导致未定义行为)。

3. 指针与引用(基础掌握)

(1)指针

定义与取址

  • 指针变量存储变量的内存地址:int a = 10; int *p = &a;p 是指针,&aa 的地址)。
  • 解引用:*p 表示指针指向的变量的值(cout << *p; 输出10)。

指针运算

  • 指针加减整数:p++ 等价于 p = p + sizeof(int)(移动一个 int 类型的大小,4字节)。
  • 指针与数组的关系:数组名是首元素的地址(int a[5]; int *p = a; 等价于 p = &a[0])。

指针作为函数参数

void modify(int *x) {
    *x = 100;  // 修改指针指向的变量的值
}

int main() {
    int a = 5;
    modify(&a);  // 传递a的地址
    cout << a;   // 输出100(原变量被修改)
    return 0;
}
(2)引用

定义与绑定

  • 引用是变量的别名,必须初始化且不可重新绑定:int a = 10; int &ref = a;refa 的别名)。
  • 对引用的修改直接影响原变量:ref = 20; 等价于 a = 20;

引用作为函数参数

void swap(int &x, int &y) {  // 引用传递
    int temp = x;
    x = y;
    y = temp;
}

int main() {
    int a = 3, b = 5;
    swap(a, b);  // 直接修改a和b的值
    cout<< a << " " << b;  // 输出5 3
    return 0;
}

指针 vs 引用

特性 指针 引用
初始化 可后初始化(int *p; p = &a; 必须初始化(int &ref = a;
空值 可指向 nullptr 不可为空
重新绑定 可重新指向其他变量 不可重新绑定
内存占用 占用内存(存储地址) 不占用额外内存(是别名)

二、函数(核心能力)

1. 函数基础

定义与调用

  • 函数声明(原型):告诉编译器函数的存在(避免编译错误)。
    int add(int a, int b);  // 函数声明(参数类型、返回值类型)
    int main() {
        int c = add(3, 5);   // 函数调用
        return 0;
    }
    int add(int a, int b) {  // 函数定义
        return a + b;
    }
    

返回值

  • 一个函数只能返回一个值,但可通过指针/引用参数返回多个值。
    void calc(int a, int b, int &sum, int &diff) {  // 通过引用返回和与差
        sum = a + b;
        diff = a - b;
    }
    

2. 参数传递进阶

值传递:函数接收的是实参的副本,修改形参不影响实参。
指针传递:函数接收实参的地址,通过解引用修改原变量。
引用传递:函数接收实参的别名,直接修改原变量(更安全,无空指针风险)。

3. 递归函数(重点)

定义:函数直接或间接调用自身,必须包含终止条件。

例题:递归计算阶乘

long long factorial(int n) {
    if (n == 0 || n == 1) {  // 终止条件
        return 1;
    } else {
        return n * factorial(n - 1);  // 递归调用(n * (n-1)!)
    }
}

例题:汉诺塔问题(递归经典)
问题描述:将A柱的n个盘子移动到C柱(借助B柱),每次只能移动1个盘子,大盘不能放在小盘上。
递归思路

  • 将n-1个盘子从A移到B(借助C)。
  • 将第n个盘子从A移到C。
  • 将n-1个盘子从B移到C(借助A)。

代码实现

void hanoi(int n, char A, char B, char C) {
    if (n == 1) {  // 终止条件:仅1个盘子
        cout << "移动盘子1从"<< A << "到"<< C << endl;
        return;
    }
    hanoi(n-1, A, C, B);  // 将n-1个盘子从A移到B(借助C)
    cout << "移动盘子"<< n << "从"<< A << "到"<< C << endl;
    hanoi(n-1, B, A, C);  // 将n-1个盘子从B移到C(借助A)
}

int main() {
    hanoi(3, 'A', 'B', 'C');  // 测试3层汉诺塔
    return 0;
}

输出结果

移动盘子1从A到C
移动盘子2从A到B
移动盘子1从C到B
移动盘子3从A到C
移动盘子1从B到A
移动盘子2从B到C
移动盘子1从A到C

三、结构体与联合体(基础应用)

1. 结构体(struct)

定义与成员访问

struct Student {
    string name;
    int age;
    double score;
};

int main() {
    Student s1;  // 定义结构体变量
    s1.name = "张三";
    s1.age = 15;
    s1.score = 90.5;

    Student *p = &s1;  // 结构体指针
    cout << p->name;   // 用->访问成员(等价于 (*p).name)
    return 0;
}

结构体数组

Student stu[3] = {
    {"张三", 15, 90.5},
    {"李四", 16, 85.0},
    {"王五", 15, 92.0}
};

// 遍历结构体数组
for (int i = 0; i < 3; i++) {
    cout << stu[i].name << "的成绩:" << stu[i].score << endl;
}

结构体作为函数参数

// 值传递:复制整个结构体(效率低)
void printStudent(Student s) {
    cout << s.name << " " << s.age << endl;
}

// 引用传递:直接访问原结构体(效率高)
void modifyScore(Student &s, double newScore) {
    s.score = newScore;
}

2. 联合体(union)

定义与特性

union Data {
    int i;
    float f;
    char c;
};

int main() {
    Data d;
    d.i = 10;
    cout << d.f;  // 输出乱码(i和f共享内存,修改i会影响f)
    d.c = 'a';
    cout << d.i;  // 输出97('a'的ASCII码)
    return 0;
}

注意:联合体的内存大小等于最大成员的大小(如上述 Data 大小为4字节,与 intfloat 一致)。


四、算法基础(核心考查)

1. 枚举(穷举)算法

核心:遍历所有可能的解,筛选符合条件的。
优化技巧:缩小枚举范围,减少计算量。

例题:百钱买百鸡(公鸡5元/只,母鸡3元/只,小鸡1元/3只,用100元买100只鸡)
思路:枚举公鸡(x)、母鸡(y)数量,小鸡数量为 100-x-y,需满足:
5x + 3y + (100-x-y)/3 = 100,且 x,y,100-x-y 均为非负整数。

代码实现

int main() {
    for (int x = 0; x <= 20; x++) {       // 公鸡最多20只(5*20=100元)
        for (int y = 0; y <= 33; y++) {   // 母鸡最多33只(3*33=99元)
            int z = 100 - x - y;          // 小鸡数量
            if (z % 3 == 0 && 5*x + 3*y + z/3 == 100) {
                cout << "公鸡:"<< x << " 母鸡:"<< y << " 小鸡:"<< z << endl;
            }
        }
    }
    return 0;
}

输出结果(3组解):

公鸡:0 母鸡:25 小鸡:75  
公鸡:4 母鸡:18 小鸡:78  
公鸡:8 母鸡:11 小鸡:81  

2. 排序算法

(1)冒泡排序

原理:相邻元素比较,大的往后移(升序),每轮将最大值“冒”到末尾。
代码实现(升序):

void bubbleSort(int arr[], int len) {
    for (int i = 0; i < len-1; i++) {        // 轮数:len-1轮(最后一轮只剩1个元素)
        bool swapped = false;                // 优化:若某轮无交换,已有序
        for (int j = 0; j < len-1-i; j++) {  // 每轮比较次数:len-1-i次(末尾已排序)
            if (arr[j] > arr[j+1]) {
                swap(arr[j], arr[j+1]);
                swapped = true;
            }
        }
        if (!swapped) break;  // 提前结束
    }
}
(2)选择排序

原理:每轮找到未排序部分的最小值,与当前位置交换。
代码实现(升序):

void selectionSort(int arr[], int len) {
    for (int i = 0; i < len-1; i++) {
        int min_idx = i;
        for (int j = i+1; j < len; j++) {
            if (arr[j] < arr[min_idx]) {
                min_idx = j;  // 记录最小值索引
            }
        }
        swap(arr[i], arr[min_idx]);  // 交换当前位置与最小值
    }
}

对比

  • 冒泡排序:稳定(相等元素顺序不变),每轮必交换。
  • 选择排序:不稳定(可能打乱相等元素顺序),每轮仅交换一次。

3. 查找算法

(1)二分查找(折半查找)

前提:数组有序(升序或降序)。
原理:每次取中间元素比较,缩小查找范围。

代码实现(升序数组):

// 左闭右闭区间 [left, right]
int binarySearch(int arr[], int len, int target) {
    int left = 0, right = len - 1;
    while (left <= right) {          // 终止条件:left > right(无元素可查)
        int mid = left + (right - left)/2;  // 防止整数溢出(等价于 (left+right)/2)
        if (arr[mid] == target) {
            return mid;  // 找到目标,返回索引
        } else if (arr[mid] < target) {
            left = mid + 1;  // 目标在右半部分
        } else {
            right = mid - 1;  // 目标在左半部分
        }
    }
    return -1;  // 未找到
}

注意:若数组无序,需先排序(排序的时间复杂度为 O(nlog⁡n)O(n\log n)O(nlogn),可能影响整体效率)。


五、文件操作(新增重点)

1. 文件打开与关闭

头文件#include <fstream>(输入输出文件流)。
文件流类

  • ifstream:输入文件流(读文件)。
  • ofstream:输出文件流(写文件)。

打开文件

ifstream fin;          // 定义输入文件流对象
fin.open("data.txt");  // 打开文件(默认模式:ios::in)

ofstream fout("info.txt", ios::out | ios::app);  // 定义时直接打开(写模式+追加模式)

打开模式(常用):

模式标志 描述
ios::in 读模式(默认用于 ifstream
ios::out 写模式(默认用于 ofstream,会覆盖原文件)
ios::app 追加模式(写数据到文件末尾)
ios::trunc 截断文件(写模式时清空原内容,默认与 ios::out 搭配)

检查文件是否打开成功

if (!fin.is_open()) {
    cerr << "文件打开失败!" << endl;
    return 1;
}

2. 文本文件读写

写入数据ofstream):

ofstream fout("student.txt");
fout << "姓名:张三" << endl;
fout << "年龄:15" << endl;
fout << "成绩:90.5" << endl;
fout.close();  // 必须关闭文件,确保数据写入磁盘

读取数据ifstream):

ifstream fin("student.txt");
string line;
while (getline(fin, line)) {  // 读取一行(包含空格)
    cout << line << endl;
}

// 或按空格分割读取(适用于无空格的数据)
int age;
double score;
fin >> age >> score;  // 读取数值(跳过空格和换行)
fin.close();

注意事项

  • 读取数值时,>> 会自动跳过空白符(空格、换行),适合结构化数据(如 10 20 30)。
  • 读取字符串时,>> 会以空格为分隔符,若需读取整行(含空格),用 getline(fin, str)

六、编程规范与调试

1. 代码规范

  • 命名:变量/函数名用小写字母+下划线(如 student_name, calc_sum);类名用大驼峰(如 StudentInfo)。
  • 缩进:循环/条件语句的大括号单独成行,内部代码缩进4空格(或1Tab)。
  • 注释:函数开头注释说明功能、参数、返回值;复杂逻辑添加行注释。

2. 常见错误排查

  • 语法错误:编译器报错(如 error: expected ';' before '}' token),根据提示定位行号,检查分号、括号是否匹配。
  • 逻辑错误:程序运行但结果错误(如数组越界导致数据错误),通过 cout 输出中间变量排查。
  • 运行时错误:访问空指针(nullptr)、文件不存在(打开失败),添加错误检查(如 if (p == nullptr) { ... })。

总结

GESP C++ 二级考试重点考查语法应用能力算法思维,需熟练掌握数组、函数、结构体、基础算法(排序、查找)及文件操作。学习时需多敲代码、多做练习(尤其是历年真题),注意细节(如数组越界、指针空值),并通过调试掌握排错技巧。


网站公告

今日签到

点亮在社区的每一天
去签到