Lesson 5:按情况处理
用边界表验证 if、else-if、switch、关系与逻辑运算。
学习目标
- 能解释关系与逻辑表达式如何产生布尔条件,并画出短路判断路径
- 能实现互斥的 if/else-if/else 与 switch 分支,说明每个输入只落入哪个出口
- 能设计边界测试表,发现条件重叠、空洞、悬空 else 和意外贯穿
来源、版次与标准边界
“Lesson 5:按情况处理”以 SB Creative 出版社书页核定高桥麻奈著、第 5 版、2017 年 6 月 14 日发行、ISBN 978-4-7973-9259-3;出版社页面标 596 页。CiNii Books 书目与 16 课目录记录正文 xxiii+571 页,日本国会图书馆书目记录 571 页。页数口径存在前置页差异,因此本课程不拿页码充当目录证据。
“Lesson 5:按情况处理”的中文说明、示例、交互、练习和答案均为独立教学重写。公开资料只确认 16 个 Lesson 标题;本页列出的细分概念是课程教学映射,不冒充原书逐级小节。技术规则参考 ISO C++ 标准入口复核,但 2017 年教材的 Visual Studio 2017 语境不会被静默升级为 C++23;现代写法只能明确标为迁移说明。
围绕“怎样证明所有互斥分支都可达、无重叠,并覆盖业务允许的完整输入域?”,本页要求保留“输入域、谓词真值表、最小边界集合、实际分支标签和拒绝路径。”。若故障“相邻区间在边界值上同时遗漏或重叠”无法在同一输入下制造首个分岔,应拒绝当前解释,而不是追加随机样例。
为什么分支设计先从输入集合开始
条件语句让程序根据当前状态选择不同路径。初学者常直接写 if,再一边测试一边补 else if;这种写法容易让区间重叠或漏掉边界。更稳妥的方法是先列输入集合及预期分类,再把每一类翻译为布尔条件。
以考试成绩为例,输入域可以先划分为 [90,100]、[60,89]、[0,59] 和非法区间。只有分类明确,才知道 90、60、0、100、-1 与 101 分别应走哪条路径。
关系运算产生真假结果
< <= > >= == != 比较两个值,结果类型是 bool。= 是赋值,== 才是相等比较;二者字符相近但责任完全不同。
bool adult = age >= 18;
bool validScore = score >= 0 && score <= 100;
bool different = current != previous;浮点相等比较还受表示误差影响,不能把所有计算结果都机械地用 == 判断。当前阶段先掌握整型边界,再在涉及浮点算法时定义容差与误差模型。
逻辑运算组合多个条件
逻辑与 && 要求两侧都为真,逻辑或 || 只需一侧为真,逻辑非 ! 反转真假。&& 和 || 会短路:结果一旦由左侧确定,右侧不再求值。
if (index >= 0 && index < size) {
std::cout << values[index] << '\n';
}先检查 index >= 0,再检查上界。若右侧包含数组访问或函数调用,短路可保护前置条件,但不要把关键业务副作用藏在右侧;条件应负责判断,状态变化应清楚地写在分支体中。
if/else 把路径组织成二叉选择
if 语句在条件为真时执行块;配套 else 处理假路径。总是写花括号,即使块里只有一条语句,也能避免后来增加语句时产生视觉缩进与真实作用域不一致。
if (temperature >= 30) {
std::cout << "hot\n";
} else {
std::cout << "not hot\n";
}没有花括号时,else 与最近一个尚未配对的 if 结合,形成悬空 else 风险。明确块结构既服务编译器,也服务维护者。
else-if 链按顺序选择第一个命中项
分数分类可以写成从最严格上界开始的互斥链。前一条件失败后,后续分支已经隐含部分事实;因此第二项只需判断 score >= 60,不必再次写 score < 90。
if (score < 0 || score > 100) {
std::cout << "invalid\n";
} else if (score >= 90) {
std::cout << "A\n";
} else if (score >= 60) {
std::cout << "pass\n";
} else {
std::cout << "retry\n";
}顺序本身就是规则:若先写 score >= 60,95 会提前命中“pass”,永远到不了 A。每个输入只执行第一个为真的分支,因而要用边界表证明顺序覆盖完整且无意外遮蔽。
switch 适合一个离散值的精确匹配
当选择依据是同一个整型或枚举值与多个常量匹配时,switch 语句比长串 if (command == ...) 更直接。每个 case 是入口;通常用 break 离开 switch,default 处理未知值。
switch (command) {
case 1:
std::cout << "start\n";
break;
case 2:
std::cout << "stop\n";
break;
default:
std::cout << "unknown\n";
break;
}省略 break 会继续执行后续 case,称为贯穿。它有少量刻意用途,例如多个 case 共享处理,但必须结构明显并加 [[fallthrough]] 表达意图。对范围判断、复合条件或不同变量组合,仍应使用 if 系列。
边界表比随机测试更有证明力
对区间 [0,59]、[60,89]、[90,100],至少测试每个边界两侧:-1、0、59、60、89、90、100、101。随机输入可能多次落在区间内部,却一直错过真正容易出错的等号位置。
运行前先预测每个值应命中的分支,并做四列记录:输入、预期分支、实际分支、若不一致首先被破坏的条件。例如 90 若落入 pass,不要只把输出改成 A;应回到分支顺序,检查更宽的 >= 60 是否提前出现。预测把测试从“看看会发生什么”变成对规则的反证尝试,实际结果只负责验证或推翻预测。
边界测试还要记录预期分支名,而不只看输出“似乎合理”。当规则变成等级、折扣或权限时,分支标签和输入集合就是可审查契约。
还要检查每个分支是否可达:若一项更宽的条件排在前面,后续窄条件可能永远不会执行。可达性检查与边界检查共同回答两件事:合法输入有没有出口,以及每个已写出口是否真的有输入能够到达。
三步构造完整决策
第一步:划分输入集合
先写合法域、非法域和互斥分类,列出每个边界值,不急着写 if。用真值组合确认关系与逻辑条件表达同一集合。
正式节点与章专属证据
- if 语句:在“Lesson 5:按情况处理”中核对输入、状态变化、失败模式和可复现证据;第 1 个节点必须能回到“任一合法输入恰好进入一个预期分支,非法输入进入显式拒绝路径。”。
- switch 语句:在“Lesson 5:按情况处理”中核对输入、状态变化、失败模式和可复现证据;第 2 个节点必须能回到“任一合法输入恰好进入一个预期分支,非法输入进入显式拒绝路径。”。
- 关系运算符:在“Lesson 5:按情况处理”中核对输入、状态变化、失败模式和可复现证据;第 3 个节点必须能回到“任一合法输入恰好进入一个预期分支,非法输入进入显式拒绝路径。”。
- 逻辑运算符:在“Lesson 5:按情况处理”中核对输入、状态变化、失败模式和可复现证据;第 4 个节点必须能回到“任一合法输入恰好进入一个预期分支,非法输入进入显式拒绝路径。”。
先用输入合同检查本页正式节点,再在相同初值下逐步比较正常和失败轨迹,最后只启用“相邻区间在边界值上同时遗漏或重叠”完成反例与复位。三个交互都必须能独立重置,且重置后再次满足“任一合法输入恰好进入一个预期分支,非法输入进入显式拒绝路径。”。
输入与状态合同
Lesson 5:按情况处理
怎样证明所有互斥分支都可达、无重叠,并覆盖业务允许的完整输入域?
必须先声明
为if 语句声明输入类型、有效范围、对象生命周期和失败策略。
可复核证据
保存Lesson 5:按情况处理的原始输入、初值与第一条可检查诊断。
正式节点:if 语句、switch 语句、关系运算符、逻辑运算符
编译与运行轨迹
同一输入下比较正常与失败路径
- 01列出输入域
- 02写出互斥谓词
- 03生成边界表
- 04逐项核对唯一分支
不变量:任一合法输入恰好进入一个预期分支,非法输入进入显式拒绝路径。
故障定位与复位
一次只破坏一个前提
小结
- 条件是可解释为 true/false 的表达式,分支设计应先划分输入集合再编码
- 关系运算产生 bool,逻辑运算组合条件;
&&与||短路但不应隐藏关键副作用 - if/else 处理真假路径,else-if 链按顺序选择第一个命中项,花括号消除结构歧义
- switch 适合同一离散值的常量匹配,case 不会自动停止,break 与 default 必须有明确策略
- 边界值比大量随机内部值更容易发现
</<=、分支顺序和非法域错误
练习
- 问题 1:修正赋值条件。
if (count = 0)实际做什么,正确的相等判断是什么?
- 问题 2:证明分数分类。 为什么 else-if 链必须先判断
score >= 90再判断score >= 60?
- 问题 3:设计边界表。 针对合法分数 0 到 100 和 60、90 两个阈值,列出最小高价值样例。
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 条件
- 产生真假结果并决定控制流方向的表达式。
- 关系运算符
- 比较大小或相等关系并产生 bool 的运算符。
- 逻辑运算符
- 组合或反转条件的 &&、|| 与 !。
- if/else
- 按一个条件在真、假路径间选择的语句。
- switch
- 按一个离散值与多个常量 case 匹配入口的语句。