> For the complete documentation index, see [llms.txt](https://docs.1l.lol/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.1l.lol/gesp-zone/gesp/gesp-4.md).

# GESP 四级复习资料

### 🔗 1. 指针类型：定义、赋值、解引用

考纲条目 ① 理解 C++ 指针类型的概念，掌握指针类型变量的定义、赋值、解引用。

#### 1.1 什么是指针

指针是**存放另一个变量内存地址** 的变量。它本身也是一个变量，因此**指针变量本身也占用内存**（这是常见判断题考点）。

**记忆要点：** 指针 = 存储地址的变量。指针变量本身占内存；指针可以指向 void 类型。

#### 1.2 指针的三大操作符

| 运算符 | 名称     | 作用            |
| --- | ------ | ------------- |
| &   | 取地址运算符 | 取出变量的内存地址     |
| \*  | 解引用运算符 | 访问指针指向的那个变量的值 |
| ->  | 箭头运算符  | 通过结构体指针访问成员   |

#### 1.3 指针的定义、赋值与解引用

```cpp
// 1. 定义：类型 * 指针名;
int a = 10;
int *p;              // 定义一个整型指针 p

// 2. 赋值：让指针指向变量 a 的地址
p = &a;              // &a 是 a 的地址

// 3. 解引用：通过指针访问/修改 a
cout << *p << endl;   // 输出 10，*p 相当于 a
*p = 20;             // 修改了 a，a 变成 20
cout << a << endl;     // 输出 20
```

#### 1.4 指针算术（考点：指针与数组共舞）

指针 +1 是跳到**下一个同类型元素**，不是简单加 1 字节。`p+1` 实际移动 `sizeof(类型)` 个字节。

```cpp
int arr[5] = {10,20,30,40,50};
int *p = arr;          // 数组名 arr 就是首元素地址，等价于 &arr[0]
cout << *p << endl;     // 10
cout << *(p+2) << endl; // 30，p+2 跳到第 3 个元素
p++;
cout << *p << endl;     // 20，p 现在指向 arr[1]
// 三种等价写法：arr[i] == *(arr+i) == *(p+i)
```

#### 1.5 数组名与指针的区别（判断题常考）

| 对比项    | 数组名 arr        | 指针 p         |
| ------ | -------------- | ------------ |
| 是否可赋值  | 不可以（arr 是常量地址） | 可以           |
| 指向     | 永远指向首元素        | 可指向任意位置      |
| sizeof | 整个数组的字节数       | 指针本身大小（8 字节） |

**易错点：** 指针变量本身占内存（8 字节，64 位系统）。题干说「指针变量本身不占用内存」是**错误的**。

#### 1.6 空指针与 void 指针

```cpp
int *p = nullptr;   // C++11 空指针，推荐（也可用 NULL）
void *vp;              // 万能指针，可指向任意类型，但不能直接解引用
int a = 5;
vp = &a;                 // void 指针可以保存任意地址
// cout << *vp;  // 错误！void* 不能直接解引用，需类型转换
```

### 🧱 2. 结构体 & 二维/多维数组

考纲条目 ② 掌握 C++ 结构体、二维及多维数组（不包括变长数组）的基本概念及使用。

#### 2.1 结构体的定义

结构体可以把不同类型的数据组合成一个整体，相当于自定义一种新数据类型。

```cpp
struct Student {
    string name;   // 姓名
    int age;        // 年龄
    double score;  // 成绩
};    // 注意末尾分号！

// 定义结构体变量
Student s1;
s1.name = "小明";
s1.age = 14;
s1.score = 92.5;

// 初始化方式 2：直接赋值
Student s2 = {"小红", 13, 88.0};
```

#### 2.2 结构体数组

```cpp
Student stu[3] = {
    {"小明", 14, 92.5},
    {"小红", 13, 88.0},
    {"小刚", 15, 95.0}
};
for (int i = 0; i < 3; i++)
    cout << stu[i].name << " " << stu[i].score << endl;
```

#### 2.3 结构体指针

```cpp
Student s = {"小丽", 14, 90};
Student *p = &s;
cout << p->name << endl;   // 通过箭头访问成员，等价于 (*p).name
cout << (*p).age << endl; // 两种写法等价
```

**考点：** 结构体可以定义结构体数组；结构体内也可以包含数组成员；结构体相当于自定义新的数据类型。这些判断题均正确。

#### 2.4 二维数组的定义与访问

二维数组本质是「数组的数组」。`a[行][列]`，内存中按**行优先** 顺序连续存放。

```cpp
int a[3][4];   // 3 行 4 列
// 初始化
int b[2][3] = {
    {1,2,3},
    {4,5,6}
};
cout << b[1][2] << endl;  // 第 2 行第 3 列 = 6

// 双重循环遍历
for (int i = 0; i < 2; i++) {
    for (int j = 0; j < 3; j++)
        cout << b[i][j] << " ";
    cout << endl;
}
```

#### 2.5 多维数组的内存布局（真题考点）

```
**行优先存储：** 二维数组 `a[行][列]` 在内存中先存完第 0 行所有列，再存第 1 行……

元素 `a[i][j]` 的偏移量 = `i × 列数 + j`（单位：1 个元素大小）。
```

```cpp
// 例：int a[2][3]，首地址为 &a[0][0]=0x100
// 元素 a[1][2] 的地址 = 0x100 + (1*3 + 2)*4 = 0x100 + 20 = 0x114
int a[2][3];
cout << &a[1][2] - &a[0][0] << endl; // 输出 5（相差 5 个元素）
```

#### 2.6 二维字符数组（真题考点）

记录 10 个最长 99 字节的字符串：`char s[10][100];`（每行长度要 ≥ 实际需求，留出 `\0` 结尾符）。

```cpp
char s[10][100];  // 10 个字符串，每个最多 99 个字符 + \0
for (int i = 0; i < 10; i++)
    cin >> s[i];        // 逐行读入字符串
cout << s[0] << endl; // 输出第一个字符串
```

**重要规范（新大纲）：** 禁用变长数组 VLA！不能写 `int n; cin>>n; int a[n];`。应使用静态大数组：`const int N=1005; int a[N];`。

### 🧩 3. 模块化编程与函数

考纲条目 ③ 理解模块化编程思想，掌握函数（不包含匿名函数、类的括号运算符重载）的声明、定义及调用，掌握形参与实参的概念及区别。

#### 3.1 什么是模块化编程

把大程序拆分成多个小功能模块（函数），每个函数只负责一件事，方便维护和复用。

#### 3.2 函数声明、定义、调用

```cpp
// 声明：告诉编译器有这个函数（返回类型 函数名(参数类型列表);）
int add(int a, int b);

// 定义：写清函数体
int add(int a, int b) {
    return a + b;
}

// 调用
int main() {
    int x = add(3, 4);  // x = 7
    cout << x << endl;
    return 0;
}
```

#### 3.3 形参与实参

|    | 形参（形式参数）                     | 实参（实际参数）        |
| -- | ---------------------------- | --------------- |
| 位置 | 定义函数时括号里的参数                  | 调用函数时传入的值       |
| 作用 | 接收外部数据（输入口）                  | 传给函数的实际值        |
| 例子 | int add(int a, int b) 中的 a、b | add(3,4) 中的 3、4 |

**易错：** 函数调用时如果缺少实参，将不能正确运行。函数声明（返回类型/参数）不能重复不一致。

#### 3.4 函数返回值与 void

```cpp
void printHello() {          // void 表示无返回值
    cout << "Hello" << endl;
}

bool isEven(int n) {           // 返回布尔值
    return n % 2 == 0;
}
```

#### 3.5 递归函数

函数调用自己称为递归，必须有**终止条件**，否则会无限递归。

```cpp
int factorial(int n) {
    if (n <= 1) return 1;   // 终止条件
    return n * factorial(n - 1);  // 递归调用
}
```

**新大纲提示：** 四级不考匿名函数（Lambda 表达式）和仿函数（运算符重载 operator()）。需要自定义排序比较函数时，用**普通函数** 即可。

### 🌍 4. 变量作用域：全局与局部

考纲条目 ④ 掌握变量作用域的概念，理解全局变量与局部变量的区别。

#### 4.1 全局变量 vs 局部变量

| 类型   | 定义位置   | 作用域      | 生命周期    | 默认初值      |
| ---- | ------ | -------- | ------- | --------- |
| 全局变量 | 所有函数之外 | 整个程序都能访问 | 程序开始到结束 | 0         |
| 局部变量 | 函数内部   | 仅函数内部    | 函数调用期间  | 随机值（未初始化） |

```cpp
int g = 100;      // 全局变量

void f() {
    int x = 10;     // 局部变量
    g++;             // 函数内可以访问并修改全局变量
    cout << x << " " << g << endl;
}

int main() {
    f();             // 输出 10 101
    // cout << x;   // 错误！x 是 f 的局部变量，main 里访问不到
    cout << g << endl; // 101
    return 0;
}
```

#### 4.2 同名变量的遮蔽（重点）

当局部变量与全局变量同名时，函数内优先使用**局部变量**（遮蔽全局变量）。

```cpp
int x = 5;    // 全局 x

int main() {
    int x = 10;  // 局部 x 遮蔽全局 x
    cout << x << endl;      // 输出 10（用局部）
    cout << ::x << endl;    // 输出 5（:: 表示全局）
    return 0;
}
```

#### 4.3 块作用域

```cpp
int main() {
    int a = 1;
    {   // 进入新块
        int b = 2;   // b 只在这个块内有效
        cout << a << b;   // 可以访问外层 a 和本层 b
    }   // b 在这里销毁
    // cout << b;  // 错误！b 已超出作用域
    return 0;
}
```

**要点：** 每个变量都有作用域（判断题正确）。全局数组常用于大数组，因为全局数组可开较大空间且自动初始化为 0。

### 🔀 5. 函数参数传递：值 / 引用 / 指针

考纲条目 ⑤ 掌握函数参数的传递方式：C++ 值传递、引用传递、指针传递。这是四级最核心、最易错的考点。

#### 5.1 三种传参方式对比

| 方式   | 语法              | 函数内修改形参是否影响实参 | 本质     |
| ---- | --------------- | ------------- | ------ |
| 值传递  | void f(int a)   | ❌ 不影响         | 复制一份副本 |
| 引用传递 | void f(int \&a) | ✅ 影响          | 实参的别名  |
| 指针传递 | void f(int \*a) | ✅ 影响（解引用后）    | 传地址    |

#### 5.2 值传递（不影响实参）

```cpp
void change(int a) {
    a = 99;    // 修改的是副本
}

int main() {
    int x = 10;
    change(x);
    cout << x << endl;   // 输出 10（x 没变）
    return 0;
}
```

#### 5.3 引用传递（影响实参）

```cpp
void change(int &a) {
    a = 99;    // a 是实参的别名，直接改原值
}

int main() {
    int x = 10;
    change(x);
    cout << x << endl;   // 输出 99（x 被改了）
    return 0;
}
```

#### 5.4 指针传递（通过地址影响实参）

```cpp
void change(int *p) {
    *p = 99;    // 解引用修改指向的值
}

int main() {
    int x = 10;
    change(&x);          // 传 x 的地址
    cout << x << endl;   // 输出 99
    return 0;
}
```

#### 5.5 经典真题：交换两个数的值

```cpp
// 用引用实现 swap（最简单）
void swap(int &a, int &b) {
    int t = a; a = b; b = t;
}
// 用指针实现 swap
void swap2(int *a, int *b) {
    int t = *a; *a = *b; *b = t;
}
// 用值传递实现 swap —— 错误示范！交换无效
void swap3(int a, int b) {
    int t = a; a = b; b = t;   // 只交换了副本
}
```

main 中调用后： a=20 b=10 （swap 引用） a=20 b=10 （swap2 指针） a=10 b=20 （swap3 值传递，无效）

#### 5.6 数组作为函数参数（退化为指针）

```cpp
// 数组传参实际传的是首地址，函数内修改会影响原数组
void setArr(int a[], int n) {
    for (int i = 0; i < n; i++)
        a[i] = i * i;
}
int main() {
    int arr[3];
    setArr(arr, 3);
    cout << arr[1] << endl;   // 输出 1（原数组被修改）
    return 0;
}
```

**易错点：** 值传递时，函数参数也会作为新变量**占用内存**（判断题：传参时会额外占用内存，正确）。

### 🔁 6. 递推算法

考纲条目 ⑥ 掌握递推算法基本思想、递推关系式的推导以及递推问题求解。

#### 6.1 递推思想

根据**前一项（或前几项）** 推导出后一项。核心是找到**递推关系式** 和**边界条件**。

\*\*步骤：\*\*① 确定初始条件 → ② 找到递推关系 → ③ 循环递推 → ④ 输出结果

#### 6.2 经典：斐波那契数列

递推式：`f(1)=1, f(2)=1, f(n)=f(n-1)+f(n-2)`

```cpp
// 递推（循环）实现，非递归
int fib(int n) {
    if (n <= 2) return 1;   // 边界
    int a = 1, b = 1, c;
    for (int i = 3; i <= n; i++) {
        c = a + b;   // 递推关系
        a = b; b = c;
    }
    return c;
}
```

#### 6.3 一维递推：爬楼梯问题

一次能上 1 级或 2 级台阶，到第 n 级有几种方法？

递推式：`dp[1]=1, dp[2]=2, dp[n]=dp[n-1]+dp[n-2]`

```cpp
int climbStairs(int n) {
    if (n <= 2) return n;
    int a = 1, b = 2, c;
    for (int i = 3; i <= n; i++) {
        c = a + b;
        a = b; b = c;
    }
    return c;
}
```

#### 6.4 二维递推：杨辉三角

递推式：`a[i][0]=1, a[i][i]=1, a[i][j]=a[i-1][j-1]+a[i-1][j]`

```cpp
const int N = 30;
long long a[N][N];

int main() {
    int n; cin >> n;
    for (int i = 0; i < n; i++) {
        a[i][0] = a[i][i] = 1;   // 边界
        for (int j = 1; j < i; j++)
            a[i][j] = a[i-1][j-1] + a[i-1][j];  // 递推
    }
    for (int i = 0; i < n; i++) {
        for (int j = 0; j <= i; j++)
            cout << a[i][j] << " ";
        cout << endl;
    }
    return 0;
}
```

输入 5 输出： 1 1 1 1 2 1 1 3 3 1 1 4 6 4 1

#### 6.5 递推 vs 递归

|    | 递推            | 递归             |
| -- | ------------- | -------------- |
| 方向 | 从已知推向未知（自底向上） | 从问题反推到边界（自顶向下） |
| 实现 | 循环            | 函数调用自身         |
| 效率 | 高             | 可能有大量重复计算，慢    |

### 📊 7. 排序算法概念与特性

考纲条目 ⑦ 掌握排序算法的概念，了解内排序和外排序的概念及差别，理解排序算法的时间复杂度、空间复杂度、使用场景以及稳定性。

#### 7.1 内排序 vs 外排序

|      | 内排序    | 外排序                  |
| ---- | ------ | -------------------- |
| 数据位置 | 全部在内存中 | 数据量大，部分在内存、部分在外存（磁盘） |
| 特点   | 速度快    | 需频繁读写磁盘，用于超大数据量      |

#### 7.2 稳定性（高频考点）

**稳定排序定义：** 对于相等关键字的元素，排序后**相对顺序保持不变**，则该排序是稳定的。

| 排序算法 | 稳定性   | 平均时间复杂度 | 空间复杂度 |
| ---- | ----- | ------- | ----- |
| 冒泡排序 | ✅ 稳定  | O(n²)   | O(1)  |
| 插入排序 | ✅ 稳定  | O(n²)   | O(1)  |
| 选择排序 | ❌ 不稳定 | O(n²)   | O(1)  |

**记忆口诀：**「插冒稳定，选择不稳」。选择排序因为会交换到很远的位置，可能破坏相同元素的相对顺序。

#### 7.3 稳定性应用场景（扑克牌案例）

按花色排序后再按点数排序：如果用**稳定** 排序，第二趟按点数排序时，相同点数的牌会保持第一趟花色顺序。若用不稳定排序，花色顺序会乱。

### ⚙️ 8. 冒泡 / 插入 / 选择排序实现

考纲条目 ⑧ 掌握冒泡、插入、选择排序的算法思想、排序步骤及代码实现。

#### 8.1 冒泡排序（稳定）

相邻元素两两比较，把大的往后「冒」，每一趟确定一个最大值放在末尾。

```cpp
void bubbleSort(int a[], int n) {
    for (int i = 0; i < n - 1; i++) {        // n-1 趟
        bool swapped = false;
        for (int j = 0; j < n - 1 - i; j++) {  // 每趟少比较一个
            if (a[j] > a[j+1]) {
                swap(a[j], a[j+1]);
                swapped = true;
            }
        }
        if (!swapped) break;   // 优化：无交换则已有序
    }
}
```

#### 8.2 选择排序（不稳定）

每一趟选出剩余部分的最小值，放到当前位置。

```cpp
void selectSort(int a[], int n) {
    for (int i = 0; i < n - 1; i++) {
        int minIdx = i;                 // 假设当前位置最小
        for (int j = i + 1; j < n; j++) {
            if (a[j] < a[minIdx])
                minIdx = j;                 // 找真正最小的下标
        }
        if (minIdx != i)
            swap(a[i], a[minIdx]);          // 交换到前面
    }
}
```

#### 8.3 插入排序（稳定）

像整理扑克牌，把每个元素插入到前面已排好序的正确位置。

```cpp
void insertSort(int a[], int n) {
    for (int i = 1; i < n; i++) {
        int key = a[i];      // 待插入元素
        int j = i - 1;
        while (j >= 0 && a[j] > key) {
            a[j+1] = a[j];   // 后移腾位置
            j--;
        }
        a[j+1] = key;         // 插入到正确位置
    }
}
```

#### 8.4 三大排序对比表

| 算法 | 思想    | 稳定性 | 最好情况  | 平均    | 最坏    |
| -- | ----- | --- | ----- | ----- | ----- |
| 冒泡 | 相邻交换  | 稳定  | O(n)  | O(n²) | O(n²) |
| 选择 | 找最小交换 | 不稳定 | O(n²) | O(n²) | O(n²) |
| 插入 | 插到有序区 | 稳定  | O(n)  | O(n²) | O(n²) |

#### 8.5 用结构体 + 自定义排序做多关键字排序（综合高频）

```cpp
struct Student {
    string name;
    int score;
};

// 自定义比较函数：先按分数降序，分数相同按姓名字典序
bool cmp(const Student &a, const Student &b) {
    if (a.score != b.score) return a.score > b.score;
    return a.name < b.name;
}

int main() {
    Student s[3] = {{"A",90},{"B",95},{"C",90}};
    sort(s, s+3, cmp);   // std::sort + 自定义比较函数
    for (int i=0;i<3;i++)
        cout << s[i].name << " " << s[i].score << endl;
    return 0;
}
```

B 95 A 90 C 90

**四级推荐：** 自定义排序用**普通比较函数** 传给 sort，不要用 Lambda 表达式（新大纲四级不考）。

### ⏱️ 9. 算法复杂度估算

考纲条目 ⑨ 简单算法复杂度的估算，含多项式、指数复杂度。

#### 9.1 时间复杂度的概念

用大 O 表示法描述算法运行时间随输入规模 n 增长的**量级**，忽略常数和低阶项。

#### 9.2 常见复杂度从快到慢

| 复杂度        | 含义   | 典型代码             |
| ---------- | ---- | ---------------- |
| O(1)       | 常数时间 | 访问数组元素 a\[i]     |
| O(log n)   | 对数时间 | 二分查找             |
| O(n)       | 线性时间 | 单层 for 循环        |
| O(n log n) | 线性对数 | 归并/快排、sort       |
| O(n²)      | 平方时间 | 双层 for 循环、三大简单排序 |
| O(2ⁿ)      | 指数时间 | 纯递归斐波那契          |

#### 9.3 如何分析嵌套循环

```cpp
// 单层循环 → O(n)
for (int i = 0; i < n; i++) { ... }

// 双层循环，两层都到 n → O(n²)
for (int i = 0; i < n; i++)
    for (int j = 0; j < n; j++) { ... }

// 双层循环，第二层到 i → 仍为 O(n²)，因为 1+2+...+n ≈ n²/2
for (int i = 0; i < n; i++)
    for (int j = 0; j < i; j++) { ... }
```

#### 9.4 多项式复杂度

多项式复杂度指形如 O(n^k) 的复杂度（k 为常数），如 O(n)、O(n²)、O(n³)。**指数复杂度** O(2ⁿ) 增长极快，n 稍大就不可行。

**真题：** 冒泡排序平均时间复杂度为 O(n²)。对 N 个元素执行插入排序通常也是 O(N²)。

### 📁 10. 文件操作与重定向

考纲条目 ⑩ 掌握文件操作中的重定向，实现文件读写操作，了解文本文件的分类，掌握写操作、读操作、读写操作。

#### 10.1 文件重定向（最简单方式）

通过重定向，可以把程序对 `cin` 的读入来自文件，对 `cout` 的输出写入文件。常用于比赛调试和记录日志。

**命令行重定向：**

读入文件：`./program < input.txt`

输出到文件：`./program > output.txt`

追加重定向：`./program >> log.txt`

**说明：** 通过重定向把 cout 的内容输出到文件，是常用的记录程序运行日志的方法（判断题正确）。

#### 10.2 使用 freopen 重定向（代码内方式）

```cpp
#include <cstdio>
int main() {
    // 把标准输入输出重定向到文件
    freopen("in.txt", "r", stdin);    // 从 in.txt 读入
    freopen("out.txt", "w", stdout);  // 输出到 out.txt

    int a, b;
    cin >> a >> b;
    cout << a + b << endl;
    return 0;
}
```

#### 10.3 使用文件流 fstream（标准方式）

```cpp
#include <fstream>
int main() {
    // 写文件
    ofstream fout("data.txt");
    fout << "Hello" << 123 << endl;
    fout.close();

    // 读文件
    ifstream fin("data.txt");
    string s; int n;
    fin >> s >> n;      // 从文件读取
    cout << s << " " << n << endl;
    fin.close();
    return 0;
}
```

#### 10.4 文件读写模式

| 模式                  | 含义        |
| ------------------- | --------- |
| r / ios::in         | 只读        |
| w / ios::out        | 只写（清空原内容） |
| a / ios::app        | 追加写       |
| ios::in \| ios::out | 读写        |

**读文件细节坑：** 用 `cin >>` / `fin >>` 读取时默认以空格和换行作为分隔符。若文件内容带标点/换行需逐词处理，可用 `getline` 读整行。

### ⚠️ 11. 异常处理机制

考纲条目 ⑪ 了解异常处理机制，掌握异常处理的常用方法（try / throw / catch）。

#### 11.1 异常处理三关键字

| 关键字   | 作用         |
| ----- | ---------- |
| try   | 包裹可能出错的代码块 |
| throw | 抛出异常       |
| catch | 捕获并处理异常    |

#### 11.2 基本语法与示例

```cpp
int divide(int a, int b) {
    if (b == 0)
        throw "除数不能为 0!";   // 抛出异常
    return a / b;
}

int main() {
    try {
        int r = divide(10, 0);
        cout << r << endl;
    } catch (const char* msg) {
        cout << "捕获到异常: " << msg << endl;
    }
    cout << "程序继续执行" << endl;
    return 0;
}
```

捕获到异常: 除数不能为 0! 程序继续执行

#### 11.3 异常处理的要点

* 若 try 块中发生异常，程序跳到对应的 catch 块处理。
* catch 捕获后，程序继续执行（不会崩溃）。
* 可捕获不同类型的异常（如 int、string、自定义类型）。
* throw 可抛出任意类型。

  **判断题易错：** 一个可能抛出异常的函数，调用它的位置没有在 try 子句中，**不会** 引起编译错误（编译能通过，只是运行可能崩溃）。题干说「会引起编译错误」是**错误的**。

### 🎯 12. 综合应用：真题融合题型

四级编程题常把 **结构体 + 自定义排序 + 贪心/递推** 融合在一起。这是出题的核心方向。

#### 12.1 综合例：多关键字排序 + 结构体

**题目：** 输入 n 名学生的姓名和两科成绩，按总分从高到低排序；总分相同按第一科成绩高者优先；再相同按姓名字典序。

```cpp
#include <bits/stdc++.h>
using namespace std;

struct Student {
    string name;
    int a, b;
    int total;   // 总分
};

bool cmp(const Student &x, const Student &y) {
    if (x.total != y.total) return x.total > y.total;
    if (x.a != y.a) return x.a > y.a;
    return x.name < y.name;
}

int main() {
    int n; cin >> n;
    Student s[1000];
    for (int i = 0; i < n; i++) {
        cin >> s[i].name >> s[i].a >> s[i].b;
        s[i].total = s[i].a + s[i].b;
    }
    sort(s, s + n, cmp);
    for (int i = 0; i < n; i++)
        cout << s[i].name << " " << s[i].total << endl;
    return 0;
}
```

#### 12.2 综合例：二维数组 + 指针

**题目：** 求一个 n×m 矩阵中每行最大值的和。

```cpp
const int N = 105;
int a[N][N];

int main() {
    int n, m; cin >> n >> m;
    for (int i = 0; i < n; i++)
        for (int j = 0; j < m; j++)
            cin >> a[i][j];
    int sum = 0;
    for (int i = 0; i < n; i++) {
        int mx = a[i][0];
        for (int j = 1; j < m; j++)
            if (a[i][j] > mx) mx = a[i][j];
        sum += mx;
    }
    cout << sum << endl;
    return 0;
}
```

#### 12.3 综合例：贪心 + 排序（优先购买）

**思路：** 按规则排序后，遍历商品，只要预算够就买（贪心）。

```cpp
struct Item { string name; int price, priority; };
bool cmp(const Item &a, const Item &b) {
    if (a.priority != b.priority) return a.priority < b.priority;
    if (a.price != b.price) return a.price < b.price;
    return a.name < b.name;
}
// 排序后遍历：if (budget >= item.price) { buy; budget -= item.price; }
```

#### 12.4 备考路线图

1. 指针与结构体：指针定义/解引用、结构体数组
2. 二维数组：行列遍历、内存布局
3. 函数：声明/定义/调用、三种传参、作用域
4. 递推：斐波那契、爬楼梯、杨辉三角
5. 排序：冒泡/选择/插入 + 稳定性 + 复杂度
6. 综合：结构体 + 自定义排序 + 贪心
7. 其他：文件操作、异常处理


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.1l.lol/gesp-zone/gesp/gesp-4.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
