Skip to content

第 8 章 M1 综合实战——从零到你的第一个计算器


🏗️ 前情回顾:第 1~7 章知识点散落一地——变量、类型、输入输出、运算、数学函数。现在把它们串成一条项链,从零写出一个能用的计算器。


🎯 本章目标

学完这一章,你能:

  • 把变量、类型、输入输出、运算符组合成一个完整程序
  • 理解"输入 → 处理 → 输出"这个 IPO 模型
  • 独立写出一个能加减乘除的计算器
  • 学会处理"除数为 0"这种异常情况
  • 写出结构清晰、有注释、有缩进的好代码

📖 故事引入

小明学编程刚满一个月。有一天他妈妈问他:"你学了这么久编程,能做个什么东西出来?"

小明愣了一下。他会写 int a = 5;,会用 cin >> x;,会算 a + b……但这些东西好像都是"零件",从来没有组装成一个完整的"机器"。

他决定做一个计算器——妈妈输入两个数字和一个运算符,程序自动算出结果。这个想法简单,但能用的计算器要串起 M1 所有知识:变量存数字,cin 接收输入,运算符判断做哪种运算,cout 输出结果。

一个小时后,小明把笔记本电脑抱到妈妈面前:"妈,试试我的计算器。"

妈妈输入"15 / 0",程序没有崩溃,而是优雅地提示:"除数不能为 0!"

妈妈惊讶地看了小明一眼:"你还知道考虑这个?"

小明笑了——这就是编程教会他的第一件事:写程序不只是让功能跑起来,还要考虑它会遇到的所有可能。


🧱 知识讲解

8.1 程序的骨架:IPO 模型

所有程序——不管多复杂——本质上都遵循一个三步骤模式:

输入(Input) → 处理(Process) → 输出(Output)

你用过计算器 APP 吧?你按数字和运算符(输入),它内部运算(处理),屏幕上显示出结果(输出)。这就是 IPO。

我们写计算器,也按这个骨架搭:

I:cin 接收两个数 + 一个运算符
P:根据运算符,做对应的计算
O:cout 输出结果

先把骨架画清楚,再往里面填肉。这比一上来就埋头敲代码高效十倍。

8.2 用选择结构判断运算符

用户可能输入 +-*/ 四种运算符中的一种。怎么让程序"知道"该做哪种运算?

cpp
if (op == '+') {
    result = a + b;
} else if (op == '-') {
    result = a - b;
} else if (op == '*') {
    result = a * b;
} else if (op == '/') {
    result = a / b;
} else {
    cout << "不支持的运算符!" << endl;
    return 1;   // 异常退出
}

注意运算符用 char 类型存储——一个字符变量,刚好装一个 '+''*'

8.3 守护除法:除数为 0 怎么办?

这是计算器里最容易被攻击的 bug。15 ÷ 0 在数学上没有意义,在 C++ 里会导致程序崩溃(浮点除法得到 inf,整数除法直接炸)。

写除法之前,加一堵"防火墙":

cpp
if (op == '/') {
    if (b == 0) {
        cout << "错误:除数不能为 0!" << endl;
    } else {
        result = a / b;
    }
}

这叫做防御性编程——假设用户会犯所有可能的错,提前挡住。养成这个习惯,你的代码质量就能甩开 80% 的初学者。

8.4 完整代码:迷你计算器

先看一个"典型初学者版本"——你能找出几个问题?

cpp
// ❌ 错误版:隐患重重
#include <iostream>
using namespace std;

int main() {
    int a, b, result;    // 用 int → 不支持小数
    char op;
    cin >> a >> op >> b;
    if (op == '+') result = a + b;
    if (op == '-') result = a - b;
    if (op == '*') result = a * b;
    if (op == '/') result = a / b;   // 没检查除数为 0!
    cout << result << endl;
    return 0;
}

问题清单:① int 不支持小数 ② 除法没检查除数 ③ 没有处理非法运算符 ④ 用多个 if 而不是 else if(效率低)

✅ 把前面的零件组装起来——正确的版本:

cpp
#include <iostream>
using namespace std;

int main() {
    double a, b, result;   // 用 double 支持小数
    char op;               // 用 char 存运算符

    cout << "=== 迷你计算器 ===" << endl;
    cout << "请输入算式(如 5 + 3):" << endl;
    cin >> a >> op >> b;   // 一次读三个:数 运算符 数

    // 根据运算符选择计算方式
    if (op == '+') {
        result = a + b;
    } else if (op == '-') {
        result = a - b;
    } else if (op == '*') {
        result = a * b;
    } else if (op == '/') {
        if (b == 0) {
            cout << "错误:除数不能为 0!" << endl;
            return 1;
        }
        result = a / b;
    } else {
        cout << "错误:不支持的运算符 " << op << endl;
        return 1;
    }

    cout << a << " " << op << " " << b << " = " << result << endl;
    return 0;
}

💡 cin >> a >> op >> b; 一行读三个值——程序会智能地跳过空格,把数字读给 a,把字符读给 op,把下一段数字读给 b。这就是 C++ 输入流的便利之处。

8.5 好代码的三个习惯

写完程序不是终点,"写得好"才是。从你的第一个完整程序开始,养成三个习惯:

① 有意义的变量名

double a, b, c; — 一个月后自己都看不懂 ✅ double num1, num2, result; — 一眼就知道是干嘛的

② 适当的注释

不是每行都写注释,而是在关键位置写:

cpp
// 除法需检查除数不为 0
if (b == 0) { ... }

③ 一致的缩进

每一层 {} 缩进 2 或 4 格,让代码像目录树一样层次分明。大多数 IDE 按一下 Tab 键自动缩进——养成敲 { 就顺手缩进的习惯。


✋ 动手试试

试试 1:把上面的完整代码敲进你的 IDE,运行三遍,分别测试:

  • 10 + 3(正常加法)
  • 7 / 0(除零检测)
  • 9 ^ 2(非法运算符)

观察输出分别是什么。

试试 2:把 double 全部换成 int,再测试 7 / 2——结果是多少?为什么会这样?(回忆第 6 章的整数除法规则。)

试试 3:你的计算器现在只支持两个数。试着扩展成支持三个数相加:用户输入 5 + 3 + 2,程序输出 10。(提示:多声明一个变量 c,在 if 里多加一条计算。)


⚠️ 你踩过这些坑吗

  • [ ] 坑 1:整数除法导致结果错误 — 用 int 存操作数,输入 7 / 2 → 输出 3。优先用 double,除非你确定用户只需要整数。
  • [ ] 坑 2:opstringstring op; 然后比较 if (op == "+")——能跑,但小题大做。单个字符用 char
  • [ ] 坑 3:忘了检查除数result = a / b;b = 0 时程序崩溃。除法前先 if (b == 0) 检查。
  • [ ] 坑 4:cin >> a >> op >> b 输入时空格不对 — 用户输入 5+3(没空格)也能正确解析,但建议加空格更清晰。

📝 练习

基础题

1. 填空题:IPO 模型中,I 代表 ____,P 代表 ____,O 代表 ____。

2. 选择题:用户输入 8 / 0,如果程序没有检查除数,会发生什么?   A. 自动输出 0   B. 程序崩溃或输出异常值   C. 输出 8   D. 自动退出

3. 读代码:以下程序输入 10 + 5 后输出什么?

cpp
int a, b;
char op;
cin >> a >> op >> b;
if (op == '+') cout << a + b;
else if (op == '-') cout << a - b;

4. 判断题

(1)程序里的除数为 0 只会让结果变成 0,不会崩溃。( ) (2)char 是存单个字符的类型,适合存运算符。( ) (3)IPO 模型只适用于计算器程序。( ) (4)cin >> a >> op >> b; 可以一次性读取数字和字符。( )

提高题

5. 编程题 — 升级计算器

在迷你计算器的基础上增加"取模运算"(%)。用户输入 17 % 5,程序输出 2。注意:取模运算也需检查除数是否为 0,且操作数必须是整数(提示:用 long long 存)。

6. 编程题 — 算式智能识别

用户输入一个完整的算式字符串,如 "15+23"(不带空格),程序自动解析出两个操作数和运算符并计算结果。提示:遍历字符串找到运算符位置,再用 substr 拆分。

7. 分析题

下面这段代码在用户输入 10 / 0 时会发生什么?如果输入 10 % 0 呢?

cpp
#include <iostream>
using namespace std;
int main() {
    int a, b; char op;
    cin >> a >> op >> b;
    if (op == '/') cout << a / b;
    if (op == '%') cout << a % b;
    return 0;
}

请指出问题并写出修改方案。

挑战题

8. 编程题 — 多步运算

让用户输入一个三步算式(如 3 + 5 * 2),程序按正确优先级计算。这题需要你提前预习下一模块的"分支判断"——怎么区分 +* 的优先级?试着用 if 先判断运算符中的 */,再判断 +-

9. 设计题

回顾 M1 全部 8 章,画一张"知识地图":每一章的关键知识点用一句话总结,并用箭头标出章节之间的依赖关系(哪一章是后面章节的基础)。完成后和同桌交换对比,看看谁的地图更清晰。


🧠 本章小结

M1 知识串烧 —— 你的第一个计算器:

【IPO 骨架】
  Input:  cin >> a >> op >> b
  Process: if-else 根据 op 选择运算
  Output: cout << result

【知识点回顾】
  变量声明    → double a, b, result; char op;
  cin/cout    → 输入算式,输出结果
  运算符      → + - * / 算术运算
  类型选择    → double(支持小数),int(整数模式)
  防御性编程  → 除数为 0 必须检查

【好代码习惯】
  有意义的变量名 | 关键位置加注释 | 一致缩进

📝 配套练习

共8题。M1收官。每题都不只考一个知识点——时间换算考取模+整除,苹果采购考多变量+算术,三角形面积考cmath+格式。学生做完这8题,M1所有技能应该能独立调用。。★核心(课堂必做) ◆拓展(课后练习) ★★★挑战(选做)

级别题号链接覆盖知识点
★ 核心J0011https://hydro.ac/d/srqc/p/J0011double、pow、printf、全章串联
◆ 拓展J0020https://hydro.ac/d/srqc/p/J0020时间换算、取模+整除、速度计算
◆ 拓展J0021https://hydro.ac/d/srqc/p/J0021int、百分比、类型选择的"读题决策"
◆ 拓展J0015https://hydro.ac/d/srqc/p/J0015算术、预测模型、变量关系
◆ 拓展J0016https://hydro.ac/d/srqc/p/J0016多变量、批量采购、算术综合
◆ 拓展J0024https://hydro.ac/d/srqc/p/J0024整除/取模、余数处理、现实建模
◆ 拓展J0025https://hydro.ac/d/srqc/p/J0025余数、边界情况、苹果虫子问题
★★★ 挑战J0023https://hydro.ac/d/srqc/p/J0023三角形面积、sqrt、格式输出、综合检验
◆ 拓展luogu-P1425https://hydro.ac/p/luogu-P1425时间计算、取模、格式化输出
◆ 拓展luogu-P1421https://hydro.ac/p/luogu-P1421整除/取模、算术综合
◆ 拓展luogu-P1150https://hydro.ac/p/luogu-P1150while、减法模拟、计数器
◆ 拓展luogu-P1887https://hydro.ac/p/luogu-P1887算术、最值、贪心启蒙
◆ 拓展luogu-P2705https://hydro.ac/p/luogu-P2705组合思维、算术

💡 练习建议:先完成 2 道★核心题,确保掌握本章基本方法;再完成 11 道◆拓展题,覆盖不同变式和细节;★★★挑战题建议在完成拓展题后再做,涉及多知识点综合。


配套练习

共8题。M1收官。每题都不只考一个知识点——时间换算考取模+整除,苹果采购考多变量+算术,三角形面积考cmath+格式。学生做完这8题,M1所有技能应该能独立调用。。★核心(课堂必做) · ◆拓展(课后练习) · ★★★挑战(选做)

级别题号链接覆盖知识点
★ 核心J0011https://hydro.ac/d/srqc/p/J0011double、pow、printf、全章串联
◆ 拓展J0020https://hydro.ac/d/srqc/p/J0020时间换算、取模+整除、速度计算
◆ 拓展J0021https://hydro.ac/d/srqc/p/J0021int、百分比、类型选择的"读题决策"
◆ 拓展J0015https://hydro.ac/d/srqc/p/J0015算术、预测模型、变量关系
◆ 拓展J0016https://hydro.ac/d/srqc/p/J0016多变量、批量采购、算术综合
◆ 拓展J0024https://hydro.ac/d/srqc/p/J0024整除/取模、余数处理、现实建模
◆ 拓展J0025https://hydro.ac/d/srqc/p/J0025余数、边界情况、苹果虫子问题
★★★ 挑战J0023https://hydro.ac/d/srqc/p/J0023三角形面积、sqrt、格式输出、综合检验
◆ 拓展luogu-P1425https://hydro.ac/p/luogu-P1425时间计算、取模、格式化输出
◆ 拓展luogu-P1421https://hydro.ac/p/luogu-P1421整除/取模、算术综合
◆ 拓展luogu-P1150https://hydro.ac/p/luogu-P1150while、减法模拟、计数器
◆ 拓展luogu-P1887https://hydro.ac/p/luogu-P1887算术、最值、贪心启蒙
◆ 拓展luogu-P2705https://hydro.ac/p/luogu-P2705组合思维、算术

练习建议:先在课堂完成 1 道★核心题,掌握本章基本方法;课后完成 11 道◆拓展题,覆盖不同变式和细节;★★★挑战题建议在完成拓展题后再做,涉及多知识点综合。

自查清单

  • [ ] 我能画出 IPO 模型图
  • [ ] 我能独立写出迷你计算器的完整代码
  • [ ] 我知道除法前必须检查除数是否为 0
  • [ ] 我知道 charstring 的区别,单个字符用 char
  • [ ] 我的代码有缩进、有注释、变量名有意义
  • [ ] 我知道 M1 八个章节分别讲了什么

🚀 下章预告

你的计算器还很"傻"——每次运行只能算一道题,算完就退出。如果想连续计算多道题,需要一遍遍重新运行程序。有没有办法让程序自己循环起来,算完一道再接下一道?

下一模块 M2,我们将解锁程序的"判断力"——ifelseswitch,让你的程序不再是流水线上的机器,而是一个会做选择、能根据不同情况走不同岔路的智能体。