# 第五章 函数

> Arietids Learn · 个人课程复习笔记
> 在线阅读：https://learn.arietids.net/courses/programming/chapter-05/

## 关键考点总览

1. 函数的定义格式（返回类型、函数名、形参表、函数体）
2. 函数的声明与调用机制
3. 参数传递：值传递机制
4. 数组作为函数参数的特殊性（形参数组会调整为指针，传递的是首元素地址）
5. 变量作用域：局部变量与全局变量
6. 变量存储类别：auto、register、extern、static
7. 重载函数的定义与匹配规则
8. 函数模板的定义与使用
9. 递归函数的设计（递归出口、递归调用）
10. 默认参数与内联函数

---

## 学习目标（必考重点）

> 以下内容来自学习目标页面，是本章考核重点

- 掌握函数的**定义、声明、调用** [来源：第5章 p7，学习目标]
- 理解**内存层面工作机制** [来源：第5章 p7，学习目标]
- 理解**变量作用域** [来源：第5章 p7，学习目标]
- **会读代码，给出执行结果** [来源：第5章 p7，学习目标]
- 理解**重载函数**的使用 [来源：第5章 p7，学习目标]
- 会**递归调用**函数 [来源：第5章 p7，学习目标]

---

## 详细知识点

### 5.1 函数定义

#### 定义/语法

- **函数定义**：完成某一特定功能的程序模块 [来源：第5章 p9]

- **函数定义格式**：
  ```cpp
  返回类型 函数名(形式参数表) {
      变量定义部分
      语句部分
  }
  ```
  [来源：第5章 p10，函数定义格式]

- **return语句格式**：
  ```cpp
  return 表达式;     // 或 return (表达式);
  return;            // 不带返回值，只能用于void函数
  ```
  [来源：第5章 p10]

#### 规则/规范

- **C/C++中不允许在一个函数内部定义另外一个函数**（函数不能嵌套定义） [来源：第5章 p10，Notes]

- 返回类型指出函数返回值的类型，返回值必须是返回类型（或能够自动类型转换成返回类型） [来源：第5章 p10]

- 函数可以**没有返回值**，此时返回类型用 `void` [来源：第5章 p10]

- return语句可以用在函数内**任何需要返回的地方**，遇到return语句表示函数执行结束 [来源：第5章 p10，Notes]

#### 代码模式

- **无参数、无返回值**：
  ```cpp
  void printTriangle() {
      // 打印等腰三角形
  }
  ```
  [来源：第5章 p11]

- **有参数、无返回值**：
  ```cpp
  void printTriangle(int numofline) {
      // 根据输入调整三角形尺寸
  }
  ```
  [来源：第5章 p11]

- **有参数、有返回值**（阶乘）：
  ```cpp
  int fact(int n) {
      if (n < 0) return 0;  // 非法输入
      int s = 1;
      for (int i = 1; i <= n; ++i)
          s *= i;
      return s;
  }
  ```
  [来源：第5章 p12]

- **返回表达式结果**（判断闰年）：
  ```cpp
  bool IsLeapYear(int year) {
      return ((year%4==0)&&(year%100!=0)) || (year%400==0);
  }
  ```
  [来源：第5章 p13]

#### 易错点与辨析

- ⚠️ `return` 的功能是在**不同程序模块之间传递信息**，而 `cout` 的功能是向**硬件输出设备传递信息** [来源：第5章 p12，Notes]

- ⚠️ 阶乘计算注意**运算溢出**：13! 超出 int 范围 [来源：第5章 p12，Notes]

- ⚠️ 即使函数有返回结果，`func();` 可以独立存在，不用涉及赋值 [来源：第5章 p12，Notes]

---

### 5.2 函数的使用

#### 5.2.1 函数调用

##### 定义/语法

- **函数调用形式**：`函数名(实际参数表)` [来源：第5章 p16]

- **实际参数**：带入本次函数调用的输入。通常要求**个数与顺序匹配**；类型需满足形参要求（可发生**隐式类型转换/提升**；若存在重载，由重载决议选择“最佳匹配”）。 [来源：第5章 p16]

##### 规则/规范

- **参数传递**：将实际参数赋值给形式参数的过程 [来源：第5章 p16]

- **值传递**：实参的值被**拷贝并赋值给形参变量**，函数中对形参的修改**不影响实参** [来源：第5章 p16]

- 函数调用属于**后缀表达式**（与 `[]`、`.`、`->`、后置`++/--`同级），优先级很高，可以作为表达式的一部分 [来源：第5章 p16]

- 函数可以**嵌套调用**（使用过程中，不同函数间传递的是计算好的数值，而不是函数本身） [来源：第5章 p16，Notes]

##### 代码模式

```cpp
p(5) + p(4) + p(7)   // 计算 5! + 4! + 7!
printstar(p(4));     // 嵌套调用
```
[来源：第5章 p16]

##### 易错点与辨析

- ⚠️ 函数调用中**各实参的求值顺序不作保证**（不同编译器/优化可能不同；C++17 起各实参彼此“有序但顺序未指定”）。因此应避免在实参中写**依赖求值顺序的副作用表达式**（例如在不同实参里同时读写同一变量）。 [来源：第5章 p16，Notes]

---

#### 5.2.2 函数声明

##### 定义/语法

- **函数声明格式**：`返回类型 函数名(形式参数说明);` [来源：第5章 p17]

- 形式参数说明**只需指出参数个数和类型**，可以不给名字 [来源：第5章 p17]

##### 规则/规范

- **方法一**：定义在调用之前（不需要声明） [来源：第5章 p17]

- **方法二**：函数原型声明（定义在调用之后时使用） [来源：第5章 p17]

- 函数**声明与定义要保持一致**：返回类型、函数名、参数个数、类型和顺序必须相同 [来源：第5章 p17]

- 声明需要**分号结尾**，定义不用 [来源：第5章 p17，Notes]

##### 代码模式

```cpp
// 声明
int max(int a, int b);

// 调用
cout << max(x + 5, y - 3);

// 定义
int max(int a, int b) {
    if (a > b) return(a);
    else return(b);
}
```
[来源：第5章 p18]

##### 函数定义与声明的区别

| 比较项 | 声明 | 定义 |
|--------|------|------|
| 形式 | 类型、函数名、输入 | 类型、函数名、输入、执行内容 |
| 功能 | 告诉编译器去寻找该函数的定义 | 告诉计算机该函数要执行什么 |

[来源：第5章 p19]

---

#### 5.2.3 函数调用过程

##### 执行逻辑

函数调用的执行步骤：
1. 在调用函数(caller)中计算被调用函数(callee)每个**实际参数值**
2. 用实际参数值**初始化形式参数**
3. 依次执行函数体的每个语句，直到遇见某个return语句或函数体结束
4. 计算 return 表达式的值，并将其作为该次函数调用表达式的结果（很多情况下可发生**拷贝消除/RVO/NRVO**，不一定产生可观察的“临时对象”）
5. 返回到调用点，用该返回结果继续完成外层表达式/赋值等后续计算

[来源：第5章 p20]

##### 易错点与辨析

- ⚠️ **自动存储期**的局部变量在函数返回时结束生命周期；返回值由调用点接收（可能经过拷贝消除优化）。切勿返回局部变量的**引用/指针**。 [来源：第5章 p20，Notes]

---

#### 5.2.4 默认参数

##### 定义/语法

- C++在定义或声明函数时可以为一个或多个形参**指定默认值** [来源：第5章 p22]

```cpp
void print(int value, int base = 10);
```
[来源：第5章 p22]

##### 规则/规范

- **有声明，在声明中给出**；没有声明，在定义中给出；**不能重复定义** [来源：第5章 p22]

- **缺省参数无论有几个，都必须放在参数列表的最后** [来源：第5章 p22]

- 在函数调用时，若某个参数省略，则其后的参数**皆应省略而取其缺省值** [来源：第5章 p22]

- 同一函数在不同源文件（不同翻译单元）中的声明，默认参数应保持一致；否则会导致不同调用点使用不同默认值，接口行为不一致（C++20 起对此有更明确的约束）。 [来源：第5章 p23]

##### 代码模式

```cpp
// file1.cpp - 默认基数是10
void print(int value=1, int base=10);

// file2.cpp - 默认基数是2
void print(int value=1, int base=2);
```
[来源：第5章 p23]

---

#### 5.2.5 内联函数

##### 定义/语法

- **内联函数的定义**：在函数头部前加保留词 `inline` [来源：第5章 p24]

```cpp
inline float cube(float s) {
    return s*s*s;
}
```
[来源：第5章 p24]

##### 规则/规范

- `inline` 最初用于建议编译器进行“内联展开”，但是否展开由编译器决定；更重要的语义是允许该函数在多个翻译单元中出现**相同定义**（常用于头文件）。 [来源：第5章 p24]

- 对于**比较短小而又多次调用**的函数，可以定义为内联函数 [来源：第5章 p24]

- `inline` 函数的定义需在**使用它的翻译单元可见**（通常放在头文件）；在同一源文件中不要求写在调用语句之前。 [来源：第5章 p24]

- 定义一个函数为内联函数对于编译器**只是一个建议**（编译器可以选择不内联） [来源：第5章 p24]

---

#### 5.2.6 数组作为函数参数（重要）

##### 定义/语法

```cpp
int average(int array[5]) {
    int i, sum = 0;
    for (i = 0; i < 5; ++i)
        sum += array[i];
    return sum / 5;
}
```
[来源：第5章 p25]

##### 规则/规范

- 数组名在大多数表达式中会发生**数组到指针的衰变**，转换为指向首元素的指针 `&a[0]`；但在 `sizeof`、取地址 `&a`、`decltype(a)` 等场景不会衰变。 [来源：第5章 p28]

- 按照值传递：调用时 `score` 会**衰变为指向首元素的指针**并按值传递给形参 `array`（其类型实际被调整为 `int*`），因此二者指向同一段数组存储。 [来源：第5章 p28]

- 在函数形参中写 `T a[5]`/`T a[]` **等价于**写 `T* a`：数组形参会被调整为指针形参，因此**维度不会成为形参类型的一部分**。 [来源：第5章 p28]

- 形参 `array` 指向实参数组的同一片内存：在函数内通过 `array[i]` 修改元素会影响实参数组。 [来源：第5章 p28]

- 若形参写成 `T*`/`T[]`，传递的只有**首元素地址**，不携带数组长度；如需长度可另传 `num`，或改用 `T(&)[N]`/`std::array`/`std::span` 等保留尺寸信息。 [来源：第5章 p28]

- 需要**额外用一个整型参数来提供数组规模** [来源：第5章 p28]

##### 代码模式

```cpp
// 正确写法：额外传递数组大小
int average(int array[], int num) {
    int i, sum = 0;
    for (i = 0; i < num; ++i)
        sum += array[i];
    return sum / num;
}

// 调用
average(score, 10);
```
[来源：第5章 p30]

##### 易错点与辨析

- ⚠️ **数组参数传递的是地址**，函数内对数组元素的修改**会影响**实际数组！

```cpp
int average(int array[5]) {
    // ...
    array[3] = 90;  // 这会修改原数组！
    return sum / 5;
}

int main() {
    int score[5] = {90,65,72,83,55};
    average(score);
    cout << score[3];  // 输出 90，不是原来的 83
}
```
[来源：第5章 p27]

- ⚠️ `strcpy()` 等 C 字符串函数依赖结尾的 `\0` 判断停止，因此**不需要把长度作为参数传入**；但目标数组必须足够大，否则会发生缓冲区溢出。实务中更推荐使用 `std::string` 等更安全的方案。 [来源：第5章 p30，Notes]

---

### 5.3 变量作用域与存储类别

#### 5.3.1 变量的作用域

##### 定义/语法

- **作用域**：变量名能被引用的程序范围 [来源：第5章 p32]

- **局部变量**：函数内声明的变量以及函数的形式参数，作用域局部于函数内部；任何复合语句块内声明的变量，作用域局部于该语句块内部 [来源：第5章 p32]

- **全局变量（外部变量）**：声明/定义在所有函数之外的变量；其名字的作用域从**声明处**开始到本翻译单元结束（其他源文件如需使用，通常通过 `extern` 声明）。 [来源：第5章 p32]

##### 规则/规范

- 当碰到一个变量名，**优先用当前作用域的定义**，没有再找外部作用域（如果存在的话），最后找全局定义 [来源：第5章 p32]

- 不同作用域中**可以存在同名的不同变量** [来源：第5章 p33]

##### 执行逻辑

编译器如何确定一个名字引用的是哪个变量：
1. 编译器保存一个所有**全局声明的标识符表**
2. 编译器为每个作用域都保存一个**局部声明的标识符表**
3. 当碰到一个标识符时，**先查当前作用域**的标识符表，再查外部作用域表（如果存在的话），最后查全局标识符表

[来源：第5章 p33]

##### 例题结论

**【执行结果分析】Q1（p34）**
```cpp
int p = 1, q = 5, r = 3;  // 全局变量
void f1() { int p = 3, r = 2; q = p+q+r; } // 使用局部p,r，全局q
void f2() { p = p+q+r; }  // 全部使用全局变量
void f3() { int q; r = 2*r; q = r+p; }  // 局部q，全局r,p
```
执行结果：
- `f3: p,q,r = 1,7,6`
- `after f3: p,q,r = 1,5,6`（f3的q是局部的）
- `f1: p,q,r = 3,10,2`
- `after f1: p,q,r = 1,10,6`（f1的p,r是局部的，q是全局的）
- `f2: p,q,r = 17,10,6`
- `after f2: p,q,r = 17,10,6`

[来源：第5章 p34，Notes]

**【执行结果分析】Q2（p35）**
```cpp
int x = 1;  // 全局
int f() {
    int y = -2;
    cout << "x in f is " << x << endl;  // 输出全局x=1
    if (x) {
        int x;  // if块内的局部x
        for (x=0; x<3; x++)
            cout << "Current x and y are " << x << " and " << y << endl;
    }
    cout << "x in f is " << x << endl;  // 仍是全局x=1
    return 0;
}
int main() {
    int x = 12;  // main的局部x
    f();
    cout << "x in main is " << x << endl;  // main的x=12
}
```
输出：
```
x in f is 1
Current x and y are 0 and -2
Current x and y are 1 and -2
Current x and y are 2 and -2
x in f is 1
x in main is 12
```
[来源：第5章 p35，Notes]

---

#### 5.3.2 变量的存储类别

##### 定义/语法

- **完整的变量定义格式**：`存储类别 数据类型 变量名表;` [来源：第5章 p36]

-（传统教材表述）常见的存储类别/说明符包括：
  - `auto`（自动变量；**C++11 起不再作为存储类**，改为类型推导关键字）
  - `register`（寄存器变量；**C++17 起已移除**，不建议使用）
  - `extern`（外部链接声明，用于跨源文件引用同一实体）
  - `static`（静态存储期；在命名空间作用域也可赋予内部链接）

-（补充）现代 C++ 还提供 `thread_local`（线程存储期）。

[来源：第5章 p36]

##### （1）自动变量 auto

- 局部变量（函数内或块内定义的变量）**默认是自动变量** [来源：第5章 p37]

- 函数（语句块）被执行时，系统**自动为自动变量分配空间**，而函数返回（语句块执行结束）时，系统**自动回收**自动变量空间 [来源：第5章 p37]

```cpp
int max(int x, int y) {
    int rst;  // 等价于 auto int rst;
    rst = x>y?x:y;
    return rst;
}
```
[来源：第5章 p37]

- ⚠️ 在C++11标准中，auto已经不再作为存储类型标识符使用，现在是一个**类型指示符**，指导编译器推测变量的数据类型 [来源：第5章 p37，Notes]

##### （2）寄存器变量 register

- `register` 的本意是向编译器建议“频繁使用的局部变量可优先放入寄存器”；现代编译器通常会自行完成寄存器分配，该关键字往往无实际收益，且在 C++17 起已被移除（不建议使用）。 [来源：第5章 p38]

- **只有局部变量**可以声明为寄存器变量 [来源：第5章 p38]

- register声明只是给编译器的**一种建议**，编译器可以不理会而当作一般的自动变量 [来源：第5章 p38]

```cpp
register int x;
```
[来源：第5章 p38]

##### （3）外部变量 extern

- `extern` 用于声明具有**外部链接**的变量/函数（通常表示“在别处定义”），常用于跨源文件共享同一个全局变量；`extern` 声明本身通常不分配存储（除非带初始化）。 [来源：第5章 p39]

- 全局变量的**定义只有一次**，但可以在不同的源文件中用extern声明 [来源：第5章 p39]

- **两种使用情况**：
  1. 变量在其他文件中定义，需要在当前文件中使用
  2. 变量在同一文件中后面定义，需要在前面使用

```cpp
// file1.cpp
extern int x;  // 外部变量声明
int main() {
    f();
    cout << "in main(): x= " << x << endl;
}

// file2.cpp
int x;  // 全局变量的定义
void f() {
    cout << "in f(): x= " << x << endl;
}
```
[来源：第5章 p39]

##### （4）静态变量 static

- static可以用于**全局变量**也可以用于**局部变量** [来源：第5章 p40]

- **static用于全局变量**：使该名字具有**内部链接（internal linkage）**，只能在本翻译单元内被引用；其他源文件无法通过 `extern` 引用同一个名字来访问。 [来源：第5章 p40]

- **static用于局部变量**：该局部变量将**不会因为函数执行结束而消亡**，下一次调用也不会自动分配新的空间，而一直使用同一空间；虽然局部静态变量在函数调用结束后仍然存在，但**其他函数不能引用它** [来源：第5章 p40]

- static变量在**整个程序退出时才消亡** [来源：第5章 p40]

- 函数也可以用 `static` 声明为具有**内部链接**的函数（传统称“静态函数”），只能在本翻译单元内被引用。 [来源：第5章 p40]

- 【整理自 PPT】静态变量会被系统**自动初始化**，如果没有人为初始化，默认初始化值是**0** [来源：第5章 p40，Notes]

##### 例题结论

**【执行结果分析】Q1（p41）**
```cpp
int f(int a) {
    int b = 0;
    static int c = 3;  // 静态局部变量，只初始化一次
    b = b + 1;
    c = c + 1;
    return (a + b + c);
}
// C++中 main 必须返回 int,此示例不标准
void main() {
    int a = 2, i;
    for (i = 0; i < 3; ++i)
        cout << f(a) << ' ';
}
```
输出：`7 8 9`

分析：
- 第1次调用：b=1, c=4, return 2+1+4=7
- 第2次调用：b=1, c=5, return 2+1+5=8
- 第3次调用：b=1, c=6, return 2+1+6=9

[来源：第5章 p41，Notes]

**【执行结果分析】Q2（p41）**
```cpp
int x = 2;  // 全局
void a() { int x = 25; cout << "x in a is " << x << endl; }
void b() { static int x = 50; cout << "x in b is " << x++ << endl; }
void c() { x *= 10; cout << "x in c is " << x << endl; }
void d(int x) { cout << "x in d is " << ++x << endl; }

int main() {
    int x = 6;
    cout << "x in main is " << x << endl;
    a(); b(); c(); d(x); x++; a(); b(); c(); d(x);
    cout << "x in main is " << x << endl;
    return 0;
}
```
输出：
```
x in main is 6
x in a is 25
x in b is 50
x in c is 20
x in d is 7
x in a is 25
x in b is 51
x in c is 200
x in d is 8
x in main is 7
```
分析：
- a()：使用局部x=25
- b()：静态局部x，保持累加
- c()：使用全局x
- d()：使用参数x的副本

[来源：第5章 p41，Notes]

---

### 5.4 重载函数

##### 定义/语法

- **函数重载**：可以定义同名但参数不同的多个函数 [来源：第5章 p43]

```cpp
int f(int a1, int a2);
int f(double a1, double a2, double a3);
int f(int a1, int a2, double a3, double a4);
```
[来源：第5章 p43]

##### 规则/规范

- 在传统的C语言中，**不允许定义多个同名函数** [来源：第5章 p43]

- C++中在调用时，程序会**根据调用时给出的实参表决定具体使用哪种函数定义** [来源：第5章 p43]

##### 执行逻辑

- 编译器会给所有重名定义的函数**各自取一个内部名称**，加以区别 [来源：第5章 p43，Notes]

- 调用时先确定**可行函数**：实参个数与形参匹配（必要时可用默认参数补齐），且每个实参都能通过**隐式转换序列**匹配到对应形参。 [来源：第5章 p43，Notes]

- 在可行函数中比较隐式转换序列的优劣：通常 **精确匹配** 优于 **提升(promotion)**，优于 **标准转换(conversion)**；若无唯一最佳匹配则报二义性。默认参数主要影响“是否可行”，不直接决定“最优”。 [来源：第5章 p43，Notes]

---

### 5.5 函数模板

##### 定义/语法

- **函数模板**：实现类型的**参数化（泛型化）**，即把函数中某些形参的类型定义成参数，称为**模板参数** [来源：第5章 p45]

- 在函数调用时，编译器**根据实际参数的类型确定模板参数的值**，生成不同的模板函数 [来源：第5章 p45]

- **函数模板定义格式**：
  ```cpp
  template<模板参数类型表>
  返回类型 函数名(形式参数表) {
      // 函数体
  }
  ```
  [来源：第5章 p46]

- 模板参数既可以是**类型参数**（用 `class`/`typename` 声明，二者等价），也可以是**非类型参数**（如 `int N`）或模板模板参数等。 [来源：第5章 p46]

##### 代码模式

```cpp
template<class T1>
T1 max(T1 a, T1 b) {
    return a > b ? a : b;
}
```
[来源：第5章 p46]

##### 规则/规范

- 通常每个模板参数都需要**出现在函数形式参数表**，否则调用时需要**显式指定模板实参** [来源：第5章 p47]

```cpp
max(3, 5);      // T1 <- int
max('d', '4');  // T1 <- char
```
[来源：第5章 p47]

- 当模板参数未在形参表中使用时，需要**显式指定类型**：
  ```cpp
  template <class T1, class T2, class T3>
  T3 calc(T1 x, T2 y) {
      return x + y;
  }
  
  calc<int, char, int>(5, 'a');   // return 102
  calc<int, char, char>(5, 'a');  // return 'f'
  ```
  [来源：第5章 p47]

---

### 5.6 递归函数

##### 定义/语法

- **递归**：用了自己。直接或间接地调用了自己的函数叫做**递归函数** [来源：第5章 p49]

- 如果原始问题可以分为多个子问题，每个子问题与原问题相比**只是问题规模变小了**，则可以用递归函数来解 [来源：第5章 p49]

##### 规则/规范

- **递归需要最终终止于某个递归出口** [来源：第5章 p49]

- 递归出口没有设计好会导致：像死循环一样停不下来，更糟糕会**内存溢出，系统死机** [来源：第5章 p49，Notes]

- 递归是包含分支路径的，要确保分支之间**彼此互斥，但又能完全包含问题所涉及的集合** [来源：第5章 p49，Notes]

##### 代码模式

**阶乘的递归实现**：
```cpp
long fact(int n) {
    if (n == 0) return 1;  // 递归出口
    else return n * fact(n-1);  // 递归调用
}
```
[来源：第5章 p50]

**斐波那契数的递归实现**：
```cpp
int fib(int n) {
    if (n < 3) return 1;  // 递归出口
    return fib(n-1) + fib(n-2);  // 递归调用
}
```
[来源：第5章 p53]

**汉诺塔的递归实现**：
```cpp
void Hanoi(int n, char start, char finish, char temp) {
    if (n == 1)
        cout << start << "->" << finish << '\t';
    else {
        Hanoi(n-1, start, temp, finish);
        cout << start << "->" << finish << '\t';
        Hanoi(n-1, temp, finish, start);
    }
}
```
[来源：第5章 p59]

##### 易错点与辨析

- ⚠️ 用递归调用求斐波纳契数产生**大量重复计算**，非常低效 [来源：第5章 p54]

- ⚠️ **用循环实现**的函数没有重复计算，效率很高：
  ```cpp
  int loopfib(int n) {
      int curr = 1, prev = 1, tmp;
      for (int i = 0; i < n-2; i++) {
          tmp = curr;
          curr = curr + prev;
          prev = tmp;
      }
      return curr;
  }
  ```
  [来源：第5章 p55]

- 【整理自 PPT】理论上来讲，**所有递归算法都可以用循环实现**，但一般情况下需要依赖一些特殊的数据结构（如堆栈）才能实现 [来源：第5章 p55，Notes]

---

## 总结

1. 函数可以将一段完成独立功能的程序封装起来。通过函数名就可执行这一段功能。使用函数可以将程序**模块化** [来源：第5章 p60]

2. C++的程序是由一组函数组成。每个程序必须有一个名为**main**的函数，它对应于一般的程序设计语言中主程序 [来源：第5章 p60]

3. 函数也可以调用自己，这样的函数称为**递归函数** [来源：第5章 p60]

---

## 习题与答案

### 习题1
编写一个函数模板，判断两个一维数组是否相同。模板参数是数组类型。

**答案提示**：`typeid(a).name()` 可以获取类型名称
[来源：第5章 p61，Notes]

### 习题2
编写一个函数 `int count()`，使得第一次调用时返回1，第二次调用时返回2，即返回当前调用次数。

**答案提示**：使用**静态局部变量**
[来源：第5章 p61，Notes]

```cpp
int count() {
    static int c = 0;
    return ++c;
}
```
【整理自 PPT】（由静态局部变量特性推得）

### 习题3
设计一个基于分治法的函数，找出一组整型数中的最大值。

**答案提示**：递归，两两比较
[来源：第5章 p61，Notes]

---

## 核心考点速记表

| 考点 | 要点 |
|------|------|
| 函数定义格式 | 返回类型 + 函数名 + (形参表) + {函数体} |
| 值传递 | 形参是实参的副本，修改形参不影响实参 |
| 数组参数 | 传递的是地址，修改会影响原数组 |
| 函数声明 | 可省略形参名，只保留类型 |
| 默认参数 | 必须放在参数列表最后，省略从右向左 |
| 作用域查找顺序 | 当前作用域 → 外层作用域 → 全局 |
| static局部变量 | 不随函数结束消亡，保持上次值 |
| static全局变量 | 作用域限制在本文件 |
| extern | 声明其他文件中定义的全局变量 |
| 函数重载 | 同名不同参（个数/类型不同） |
| 函数模板 | `template<class T>` 实现泛型 |
| 递归要点 | 必须有递归出口，避免无限递归 |
