Chapter 6:Branching Statements and Logical Operators
对齐第6版 Chapter 6:掌握 if、逻辑表达式、cctype、条件运算符、switch、break/continue、数字读取循环与简单文件输入输出。
学习目标
- 能把输入域划分为互斥且完备的集合,再实现 if/else、条件运算符或 switch 并证明边界覆盖
- 能推导 &&、||、! 的逻辑表达式和短路求值,使用左侧条件保护右侧索引、指针或解析前提
- 能复现分支重叠、cctype 非法参数、switch 意外贯穿和文件打开失败,记录路径与退出原因
机制总览
Chapter 6:Branching Statements and Logical Operators:机制路径
- 1
为什么先划分输入域再写 if
直接从一个样例开始追加 if ,容易留下重叠和空洞。成绩分类应先定义合法域 [0,100] 、非法低/高域,再把合法域划为 retry、pass、excellent。每个输入必须恰好落入一个结果,条件顺序才有可审查依据。
- 2
if 语句按条件选择一条路径
if 条件求值为 true 时执行分支,else 处理剩余集合。对按阈值递减的分类,应先检查非法域,再从最高阈值向下;否则宽条件会提前吞掉更具体路径。
- 3
逻辑表达式组合条件并可能短路
&& 表示两侧都真, 表示至少一侧真, ! 反转 bool。它们从左向右求值并短路: && 左侧假时跳过右侧, 左侧真时跳过右侧。
章级决策实验
Chapter 6:Branching Statements and Logical Operators:机制与证据
切换《Chapter 6:Branching Statements and Logical Operators》的三个关键教学阶段,先解释机制,再用运行与失败证据验证结论。
选择推理阶段
当前阶段 · 为什么先划分输入域再写 if
直接从一个样例开始追加 if ,容易留下重叠和空洞。成绩分类应先定义合法域 [0,100] 、非法低/高域,再把合法域划为 retry、pass、excellent。每个输入必须恰好落入一个结果,条件顺序才有可审查依据。
可核验证据
从干净构建开始,以固定输入运行本节示例,再加入一个边界或故障场景验证「为什么先划分输入域再写 if」的状态变化。
学完《Chapter 6:Branching Statements and Logical Operators》后,应能从输入和前置条件推导状态变化,并用可重复的构建、运行或边界测试证明结果。
失效—证据矩阵
Chapter 6:Branching Statements and Logical Operators:失效与核验
为什么先划分输入域再写 if
典型失效
若只复述「为什么先划分输入域再写 if」结论而不追踪状态、所有权和失败路径,示例扩展成多文件或多对象程序后就容易偏离预期。
核验证据
从干净构建开始,以固定输入运行本节示例,再加入一个边界或故障场景验证「为什么先划分输入域再写 if」的状态变化。
if 语句按条件选择一条路径
典型失效
若只复述「if 语句按条件选择一条路径」结论而不追踪状态、所有权和失败路径,示例扩展成多文件或多对象程序后就容易偏离预期。
核验证据
从干净构建开始,以固定输入运行本节示例,再加入一个边界或故障场景验证「if 语句按条件选择一条路径」的状态变化。
逻辑表达式组合条件并可能短路
典型失效
若只复述「逻辑表达式组合条件并可能短路」结论而不追踪状态、所有权和失败路径,示例扩展成多文件或多对象程序后就容易偏离预期。
核验证据
从干净构建开始,以固定输入运行本节示例,再加入一个边界或故障场景验证「逻辑表达式组合条件并可能短路」的状态变化。
为什么先划分输入域再写 if
直接从一个样例开始追加 if,容易留下重叠和空洞。成绩分类应先定义合法域 [0,100]、非法低/高域,再把合法域划为 retry、pass、excellent。每个输入必须恰好落入一个结果,条件顺序才有可审查依据。
if 语句按条件选择一条路径
if 条件求值为 true 时执行分支,else 处理剩余集合。对按阈值递减的分类,应先检查非法域,再从最高阈值向下;否则宽条件会提前吞掉更具体路径。
std::string grade(int score) {
if (score < 0 || score > 100) return "invalid";
if (score >= 90) return "A";
if (score >= 60) return "pass";
return "retry";
}测试 -1,0,59,60,89,90,100,101,并为每条 return 记录命中次数。只有正常样例通过,无法证明边界、非法域和不可达分支。
逻辑表达式组合条件并可能短路
&& 表示两侧都真,|| 表示至少一侧真,! 反转 bool。它们从左向右求值并短路:&& 左侧假时跳过右侧,|| 左侧真时跳过右侧。
if (index >= 0 && index < count && data != nullptr) {
use(data[index]);
}先检查范围再访问,短路保证右侧只在前置条件成立时求值。条件顺序也是安全契约;把 data[index] 放到最左侧会在检查边界前访问。
cctype 按字符类别判断但有参数域
<cctype> 提供 std::isdigit、std::isalpha、std::isspace、std::toupper 等字符函数。它们的 int 参数必须是 EOF 或可表示为 unsigned char 的值;直接传负 signed char 会违反前置条件。
unsigned char code = static_cast<unsigned char>(ch);
if (std::isdigit(code)) {
++digitCount;
}字符分类比手写 ASCII 范围更能表达意图,但 locale 和编码边界仍需声明。std::toupper 返回 int,写回 char 前同样要确认表示范围。
条件运算符适合选择一个值
条件运算符 condition ? whenTrue : whenFalse 是表达式,产生一个结果,适合窄小且两侧无复杂副作用的值选择。
const char* label = score >= 60 ? "pass" : "retry";嵌套多层 ?: 会隐藏分支结构,应改成命名函数或 if/else。两候选还会参与共同类型规则,结果类型不一定与视觉上某一侧完全相同。
switch 处理一个离散值的多路选择
switch 对整型或枚举表达式与多个常量 case 匹配。case 是入口标签,不会自动创建独立作用域;若没有 break、return 或 throw,执行会贯穿到后续 case。
switch (command) {
case 's':
start();
break;
case 'q':
return;
default:
report_unknown(command);
break;
}有意贯穿应使用 [[fallthrough]] 和解释,意外遗漏 break 应由每个 case 的路径测试发现。范围判断、字符串和复合条件通常更适合 if/else。
break 与 continue 改变最近循环路径
break 离开最近循环或 switch,continue 跳到下一轮循环。它们能让异常路径提前结束,但过多跳转会让不变量和后置条件分散。
使用 continue 时确保推进步骤仍发生。while 循环若在更新前 continue,可能永远停在同一状态;for 的更新表达式仍会执行,但循环体内其他必要清理可能被跳过。
数字读取循环同时拥有格式分支
读取数字不仅有“继续/结束”,还可能遇到格式错误。while (std::cin >> value) 在每次成功提取后处理;退出时 EOF 与非数字失败需要不同消息。若要恢复,清除状态后还要消费非法记录,保证输入推进。
double value{0.0};
double sum{0.0};
while (std::cin >> value) {
sum += value;
}
if (!std::cin.eof()) {
std::cerr << "invalid numeric input\n";
}非法输入不是关系表达式能修复的问题,它属于流解析状态。分支设计要把外部失败纳入输入域,而不是默认所有字符都能变成数字。
简单文件输入输出先检查打开状态
本章初步使用 std::ifstream 把文件当输入流。路径解析和打开是第一道分支;文件不存在不能与“合法空文件”混为一谈。打开成功后,读取循环仍可能因 EOF、格式或 I/O 错误结束,Chapter 17 再完整处理。
std::ifstream input{"numbers.txt"};
if (!input) {
std::cerr << "cannot open numbers.txt\n";
return 1;
}
double number{0.0};
while (input >> number) {
process(number);
}记录当前工作目录和解析后路径,才能复现“IDE 能开、终端找不到”。打开、读取与业务验证是三层独立条件。
四类故障验证完整分支责任
先预测每个故障会命中哪条路径、哪些表达式不会求值、退出后状态是什么,再运行。分支覆盖率只能说明语句执行过,仍需边界断言证明每个输入被正确分类。
三步设计一个分支系统
第一步:划分完整输入域
列合法、非法、边界和外部失败类别,为每类指定唯一结果,再翻译成 if/else 或 switch。
小结
- 输入域先划为互斥完备类别,再实现 if/else,可避免宽条件吞掉高优先级分支
- 逻辑表达式会短路,左侧可以保护右侧前置条件,关键副作用不应藏在可能跳过的位置
- cctype 参数必须是 EOF 或 unsigned char 可表示值,字符编码和 locale 仍需声明
- 条件运算符适合选一个简单值,switch 适合一个离散值,case 不会自动停止
- 数字和文件输入有打开、格式、EOF 与业务验证分支,不能只处理正常数据
练习
- 问题 1:设计分类。 为成绩 0–100 与非法输入写互斥区间和最小边界样例。
- 问题 2:推导短路。 为什么
ptr != nullptr && index < count && ptr[index] > 0的顺序重要?
- 问题 3:区分文件结果。 缺失文件、空文件、非法首记录分别如何报告?
名词解释
名词解释
本章出现的专业名词,用大白话再讲一遍。
- 输入域划分
- 把所有输入分为互斥完备类别并指定唯一处理结果。
- if 语句
- 根据 bool 条件选择真分支与可选假分支的控制语句。
- 逻辑表达式
- 以关系结果和逻辑运算符组合条件的表达式。
- 短路求值
- 结果由左操作数确定时跳过右操作数的规则。
- cctype 字符函数
- 按字符类别判断或转换、具有受限参数域的标准函数族。
- 条件运算符
- 按条件在两个表达式中选择一个结果的三操作数表达式。
- switch 语句
- 按离散表达式与 case 常量匹配入口的多路分支。