🔷 第六章 函数
📄 一句话概括

第六章给了你一把拆刀——把一段活儿打包成函数。五块拼图:定义与声明(怎么写出来)、参数与返回值(怎么进怎么出)、调用(传值 / 嵌套 / 递归)、地盘与寿命(作用域与存储类别)、一名多玩法(重载与内联)。至此,"写一个完整小程序"的全部基本零件都到齐了。

📝 本节和前面几节的关系

第一章学搭环境、第二章学数据、第三章学运算、第四章学判断、第五章学循环——都是在一个 main 里写完整逻辑。第六章第一次让你把逻辑装进零件盒:函数。下一章(数组)开始,你会处理"一串数据",那时函数的价值会更明显——因为数组相关的操作几乎都要靠函数来传递和返回。

一、本章知识地图

块 小节 一句话要点
定义与声明 6.1 / 6.1.1 / 6.1.2 四部件:返回类型 · 函数名 · 参数表 · 函数体;分号 = 声明,花括号 = 定义;声明在调用之前
参数 6.2.1 / 6.2.2 / 6.2.3 / 6.2.4 空函数(void)· 形参是实参的复印件 · 默认参数(从右往左连续)· 可变参数(...,必须自己报个数)
返回值 6.2.5 return 两件事:交回值 + 立即结束;类型匹配 · 只能一个 · 每条路径都要有
调用 6.3 / 6.3.1 / 6.3.2 / 6.3.3 跳进去跳回来 · 传值=复印件 · 嵌套=先进后出 · 递归=出口 + 向出口靠近
地盘与寿命 6.4 / 6.7 ~ 6.7.4 作用域三层(全局/局部/块)+ 就近遮蔽;存储类别四兄弟:auto / static / register / extern
一名多玩法 6.5 / 6.6 重载:靠参数表区分(返回值不算);内联:就地展开,用空间换时间(决定权在编译器)

二、必须记住的六个结论

  1. 函数 = 给一段代码起的名字。四个部件:返回类型 · 函数名 · 参数表 · 函数体。
  2. 分号 = 声明,花括号 = 定义。声明可以写多次,定义只能有一次。
  3. 入口可以多,出口最多一个。不交结果用 void;return 交回值的同时立即结束函数。
  4. 实参是复制给形参的(传值调用)——函数改的是复印件。想改原件,要等第 7 章的指针、第 8 章的引用。
  5. 作用域 ≠ 生存期。函数里的 static 变量:外面看不见名字,命却活到程序结束。
  6. 重载靠参数表区分(返回值不算);内联只是建议(用空间换时间,决定权在编译器)。

三、动手 Demo:两关自测

第一关(写程序):成绩处理小工具

用本章学的函数,把下面四件事各写成一个函数,再在 main 里串起来:

#include <iostream>
using namespace std;

int readScore() {                      // ① 输入一个成绩
    int s;
    cout << "请输入成绩(0-100):";
    cin >> s;
    return s;
}

char getGrade(int score) {             // ② 按分数定等级
    if (score >= 90) return 'A';
    else if (score >= 80) return 'B';
    else if (score >= 60) return 'C';
    else return 'D';
}

void printResult(int score, char grade) {     // ③ 打印一行(空函数)
    cout << "  成绩 " << score << " 分,等级 " << grade << endl;
}

double average(int sum, int count) {   // ④ 求平均
    return (double)sum / count;        // 别忘了强制转换(3.3.2)
}

int main() {
    int n;
    cout << "要输入几个成绩?";
    cin >> n;

    int sum = 0;
    for (int i = 1; i <= n; i++) {
        int s = readScore();
        printResult(s, getGrade(s));   // 嵌套调用(6.3.2)
        sum += s;
    }
    cout << "平均分:" << average(sum, n) << endl;
    return 0;
}

看到什么算成功:每输入一个成绩打印一行等级,最后打印带小数的平均分。

检查清单:① 四个函数各自的返回值类型对不对;② average 里有没有做 double 转换(不转会得到整数);③ printResult 有没有写成 void。

第二关(找错):四处问题

下面这段想"找出三个数里的最大值并打印",有四处问题:

#include <iostream>
using namespace std;

void showMax(int m) {
    cout << "最大值是 " << m << endl;
}

int main() {
    int x = 10, y = 25, z = 18;
    int m = getMax(x, y, z);          // 问题①
    showMax(m);
    cout << "m 是 " << n << endl;      // 问题②
    return 0;
}

int getMax(int a, int b, int c) {
    int m = a;
    if (b > m) m = b;
    if (c > m); m = c;                // 问题③
                                      // 问题④
}

答案与理由:

# 问题 原因与修法
① getMax 没声明就在 main 里用 报 not declared;在 main 前加 int getMax(int a, int b, int c);(6.1.2)
② 变量 n 不存在 名字写错了,应为 m(2.3.1 标识符)
③ if (c > m); m = c; 多了一个分号 分号成了空语句,m = c; 变成无条件执行(4.2 的老坑)
④ 函数体末尾漏了 return m; 交回垃圾值(6.2.5);补 return m;

修好的版本:

int getMax(int a, int b, int c);          // ② 提前声明

int main() {
    int x = 10, y = 25, z = 18;
    int m = getMax(x, y, z);
    showMax(m);
    cout << "m 是 " << m << endl;
    return 0;
}

int getMax(int a, int b, int c) {
    int m = a;
    if (b > m) m = b;
    if (c > m) m = c;                     // ③
    return m;                             // ④
}

30 秒实验:把修好的程序跑一遍,应输出 最大值是 25 和 m 是 25。然后把 return m; 再注释掉,看输出变成什么(垃圾值)。

四、常见误区(本章汇总)

  1. "编译通过 = 逻辑对"——本章又是一堆"不报错陷阱":漏 return、return a, b; 只返回 b、内联不生效、extern 写成了定义。
  2. "函数里有 return 就万事大吉"——还得看是不是每条路径都有(if 里 return 了、else 没 return 就是坑)。
  3. "形参改了外面就变了"——本章永远不成立。复印件思维要刻进骨头。
  4. "static 变量和普通局部变量差不多"——差在生存期:它记着上次的值,也一直占着内存。
  5. "一个函数越长越强"——一个函数只干一件事。函数名就是这段代码的目录标题。
自测六问(点开对答案) 1. 函数定义的四个部件?——返回类型、函数名、参数表、函数体。 2. 声明和定义差什么?——声明是函数头 + 分号;定义是函数头 + 函数体(花括号)。 3. 函数的入口和出口各有几个上限?——入口可以有多个,出口最多一个。 4. 传值调用为什么改不动外面的变量?——传过去的是值的副本。 5. 递归的两个必备条件?——出口 + 每次调用都向出口靠近。 6. 作用域和生存期的区别?——作用域管名字能看见的范围;生存期管变量从生到死多久。

相关笔记