629 lines
14 KiB
Markdown
629 lines
14 KiB
Markdown
# 第三章 关系、逻辑与分支结构
|
||
|
||
## 本章内容
|
||
|
||
- 3.1 关系表达式与逻辑表达式
|
||
- 3.2 分支语句
|
||
- 3.3 分支嵌套
|
||
- 3.4 分支程序设计案例
|
||
|
||
## 3.1 关系表达式与逻辑表达式
|
||
|
||
### 核心知识
|
||
|
||
关系运算用于比较两个值,结果是 `bool` 类型,即 `true` 或 `false`。而逻辑运算符可以把多个真假条件组合成更复杂的判断。
|
||
|
||
C++ 在需要判断真假的位置,会把数值 `0` 视为假,把非零值视为真。`cout` 默认把 `true` 输出为 `1`,把 `false` 输出为 `0`。
|
||
|
||
### 关系运算符
|
||
|
||
| 运算符 | 含义 | 示例 |
|
||
| --- | --- | --- |
|
||
| `==` | 等于 | `score == 100` |
|
||
| `!=` | 不等于 | `x != 0` |
|
||
| `>` | 大于 | `a > b` |
|
||
| `<` | 小于 | `a < b` |
|
||
| `>=` | 大于等于 | `age >= 18` |
|
||
| `<=` | 小于等于 | `score <= 60` |
|
||
|
||
关系表达式示例:
|
||
|
||
```cpp
|
||
bool result1 = (10 > 5); // true
|
||
bool result2 = (3 == 4); // false
|
||
bool result3 = (7 != 0); // true
|
||
```
|
||
|
||
### 逻辑运算符
|
||
|
||
| 表达式 | 含义 | 何时为真 |
|
||
| --- | --- | --- |
|
||
| `A && B` | A 并且 B | A、B 都为真 |
|
||
| `A || B` | A 或者 B | A、B 至少一个为真 |
|
||
| `!A` | 不是 A | A 为假 |
|
||
|
||
自然语言与 C++ 表达式对照:
|
||
|
||
```cpp
|
||
// x 在闭区间 [10, 20] 内
|
||
(x >= 10) && (x <= 20)
|
||
|
||
// x 小于 0 或大于 100
|
||
(x < 0) || (x > 100)
|
||
|
||
// x 是 3 的倍数,但不是偶数
|
||
(x % 3 == 0) && (x % 2 != 0)
|
||
|
||
// x 不等于 10
|
||
!(x == 10) // 或者 x != 10
|
||
```
|
||
|
||
构建逻辑表达式时,可以按三个步骤完成:
|
||
|
||
1. **拆解条件**:把长规则拆成独立的小判断。
|
||
2. **寻找连接词**:“并且”对应 `&&`,“或者”对应 `||`,“不”对应 `!`。
|
||
3. **组合表达式**:使用括号明确每一组条件之间的关系。
|
||
|
||
### 运算优先级
|
||
|
||
常用优先级从高到低可概括为(**非算关与或**):
|
||
|
||
```text
|
||
括号 → 逻辑非 ! → 算术运算 → 关系运算 → && → ||
|
||
```
|
||
|
||
对于一些复杂表达式应主动使用括号,不要只依赖优先级记忆,例如:
|
||
|
||
```cpp
|
||
(math == 100 || english == 100) && homeworkFinished
|
||
```
|
||
|
||
### 短路求值
|
||
|
||
对于 `A && B`:
|
||
|
||
- 如果 A 为假,整个表达式一定为假,B 不再执行。
|
||
|
||
对于 `A || B`:
|
||
|
||
- 如果 A 为真,整个表达式一定为真,B 不再执行。
|
||
|
||
```cpp
|
||
int x = 0;
|
||
int y = 10;
|
||
|
||
bool a = (x != 0) && (100 / x > 2);
|
||
// 左侧为 false,右侧不执行,避免除以 0
|
||
|
||
bool b = (y > 0) || (++y > 20);
|
||
// 左侧为 true,右侧不执行,y 仍为 10
|
||
```
|
||
|
||
短路既能减少不必要的计算,也能保护右侧表达式不在非法条件下执行。
|
||
|
||
### 典型例题:闰年表达式
|
||
|
||
闰年规则为:年份能被 4 整除但不能被 100 整除,或者能被 400 整除。
|
||
|
||
拆解:
|
||
|
||
- A:`year % 4 == 0`
|
||
- B:`year % 100 != 0`
|
||
- C:`year % 400 == 0`
|
||
|
||
组合:
|
||
|
||
```cpp
|
||
bool leap = ((year % 4 == 0) && (year % 100 != 0)) || (year % 400 == 0);
|
||
```
|
||
|
||
### 常见错误
|
||
|
||
- 判断相等时写成 `=`。`=` 是赋值,`==` 才是比较。
|
||
- 写成数学连写 `5 < x < 10`。正确写法是 `x > 5 && x < 10`。
|
||
- 把逻辑与、逻辑或写成 `&`、`|`。应使用 `&&`、`||`。
|
||
- 区间题漏掉端点的等号。
|
||
- 复杂条件没有括号,导致实际的计算优先级与题意不同。
|
||
- 在逻辑表达式右侧使用 `x++` 等有副作用的操作,却忽略短路可能导致它不执行。
|
||
|
||
## 3.2 分支语句
|
||
|
||
### 核心知识
|
||
|
||
分支结构让程序根据条件选择不同的执行路径:
|
||
|
||
- `if`:只关心条件成立时需要做什么。
|
||
- `if-else`:处理二选一。
|
||
- `if-else if-else`:处理多个互斥选项,只执行首次满足的分支。
|
||
- 多个独立 `if`:每个条件分别判断,可能执行多个分支。
|
||
- `switch`:处理同一个整型或字符型表达式的多路等值选择。
|
||
|
||
### 单分支和双分支
|
||
|
||
```cpp
|
||
if (condition) {
|
||
// condition 为 true 时执行
|
||
}
|
||
```
|
||
|
||
```cpp
|
||
if (condition) {
|
||
// 条件成立时执行
|
||
} else {
|
||
// 条件不成立时执行
|
||
}
|
||
```
|
||
|
||
奇偶判断:
|
||
|
||
```cpp
|
||
if (number % 2 == 0) {
|
||
cout << "偶数";
|
||
} else {
|
||
cout << "奇数";
|
||
}
|
||
```
|
||
|
||
### 多分支
|
||
|
||
```cpp
|
||
if (condition1) {
|
||
// 第一种情况
|
||
} else if (condition2) {
|
||
// 第一种情况不成立,第二种情况成立
|
||
} else {
|
||
// 以上条件都不成立
|
||
}
|
||
```
|
||
|
||
多分支按照从上到下的顺序判断。一旦某个条件为真,程序执行对应代码块,然后跳过整条分支链。因此,条件书写顺序会影响结果。
|
||
|
||
例如对于成绩分级的实例,应首先判断更高的分数:
|
||
|
||
```cpp
|
||
if (score >= 90) {
|
||
cout << "A";
|
||
} else if (score >= 80) {
|
||
cout << "B";
|
||
} else if (score >= 60) {
|
||
cout << "C";
|
||
} else {
|
||
cout << "D";
|
||
}
|
||
```
|
||
|
||
而且对于多分支而言,后面的条件隐含了前面条件不成立。例如在执行 `score >= 80` 的分支时,已经知道 `score < 90`。
|
||
|
||
### 独立 `if` 与 `else if`
|
||
|
||
多个独立 `if` 属于“多选多”:
|
||
|
||
```cpp
|
||
if (x % 2 == 0) {
|
||
cout << "even ";
|
||
}
|
||
if (x % 3 == 0) {
|
||
cout << "multiple of 3";
|
||
}
|
||
```
|
||
|
||
当 `x` 为 6 时,两个条件都成立,两段代码都会执行。
|
||
|
||
`if-else if` 属于“多选一”:前面的分支一旦命中,后面的分支不再判断。
|
||
|
||
### `switch` 多路选择
|
||
|
||
`switch` 适合对同一个表达式进行多个等值匹配:
|
||
|
||
```cpp
|
||
switch (expression) {
|
||
case constant1:
|
||
// expression 等于 constant1
|
||
break;
|
||
case constant2:
|
||
// expression 等于 constant2
|
||
break;
|
||
default:
|
||
// 没有任何 case 匹配
|
||
break;
|
||
}
|
||
```
|
||
|
||
规则:
|
||
|
||
- `switch` 后的 `expression` 表达式应为整型、字符型的值。
|
||
- `case` 后的 `constant` 所匹配的值必须是常量,不能写范围条件,不能写变量。
|
||
- `break` 会立即跳出 `switch`。
|
||
- `default` 是可选的保底分支,作用类似多分支最后的 `else`。
|
||
- 缺少 `break` 时,程序会从匹配位置继续向下执行直到遇到 `break` 为止,这种情况称为** `case` 穿透**。
|
||
|
||
在一些特殊情况,多个 `case` 可以有意使用穿透来共用一段逻辑:
|
||
|
||
```cpp
|
||
switch (letter) {
|
||
case 'a':
|
||
case 'e':
|
||
case 'i':
|
||
case 'o':
|
||
case 'u':
|
||
cout << "元音";
|
||
break;
|
||
default:
|
||
cout << "非元音";
|
||
break;
|
||
}
|
||
```
|
||
|
||
### 常见错误
|
||
|
||
- 在 `if` 条件后误写分号:
|
||
|
||
```cpp
|
||
if (x > 0); { // 错误:分号提前结束了 if
|
||
cout << x;
|
||
}
|
||
```
|
||
|
||
- 省略花括号后添加第二条语句,误以为两条语句都受 `if` 控制。
|
||
- 把多选一写成多个独立 `if`,造成多个分支同时输出。
|
||
- 把允许多个结果的情况写成 `else if`,导致后续条件被跳过。
|
||
- 多分支先写宽松条件,导致严格条件永远没有机会命中。
|
||
- 用 `switch` 处理 `score >= 60` 等范围条件。
|
||
- `case` 后写变量、范围或重复的常量值。
|
||
- 漏写 `break`,造成意外穿透。
|
||
|
||
## 3.3 分支嵌套
|
||
|
||
### 核心知识
|
||
|
||
分支嵌套,是在一个 `if`、`else if` 或 `else` 的代码块内部再放入一个完整的分支结构。
|
||
|
||
嵌套适合“先判断大前提,再处理细分情况”的问题:
|
||
|
||
- 外层分支确定主要类别或进入资格。
|
||
- 内层分支只在外层条件成立后继续判断。
|
||
- 如果外层条件不成立,内层条件不会执行。
|
||
|
||
### 语法与执行流程
|
||
|
||
```cpp
|
||
if (外层条件) {
|
||
if (内层条件) {
|
||
// 外层条件和内层条件都成立
|
||
} else {
|
||
// 外层条件成立,但内层条件不成立
|
||
}
|
||
} else {
|
||
// 外层条件不成立
|
||
}
|
||
```
|
||
|
||
执行过程:
|
||
|
||
1. 先判断外层条件。
|
||
2. 外层条件为假时,跳过整个外层代码块。
|
||
3. 外层条件为真时,进入代码块,再判断内层条件。
|
||
4. 内层分支执行完成后,程序继续执行嵌套结构后面的代码。
|
||
|
||
### 典型例题:会员折扣
|
||
|
||
商场规则如下:
|
||
|
||
- 非会员按原价结算。
|
||
- 会员消费满 500 元打 8 折。
|
||
- 会员消费不足 500 元打 9 折。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
using namespace std;
|
||
|
||
int main() {
|
||
double amount;
|
||
bool isMember;
|
||
cin >> amount >> isMember;
|
||
|
||
double payment = amount;
|
||
|
||
if (isMember) {
|
||
if (amount >= 500) {
|
||
payment = amount * 0.8;
|
||
} else {
|
||
payment = amount * 0.9;
|
||
}
|
||
}
|
||
|
||
cout << payment;
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
**说明:** 外层先判断会员资格,只有会员才进入内层判断消费金额。非会员不进入内层,`payment` 保持原价。
|
||
|
||
### 常见错误
|
||
|
||
- 缩进混乱,无法看清 `else` 与哪个 `if` 配对。
|
||
- 外层条件不成立时仍然期待内层判断执行。
|
||
- 把所有条件都塞进多层嵌套,使代码难以阅读。能用清晰逻辑表达式解决时,不必强行嵌套。
|
||
- 忘记为各层代码块补齐花括号。
|
||
- 在不同层级重复或遗漏条件,导致某些情况没有处理。
|
||
|
||
## 3.4 分支程序设计案例
|
||
|
||
解决分支题时,可以采用四步流程:
|
||
|
||
1. **找到分岔点**:哪些条件会改变程序执行路径?
|
||
2. **写出条件表达式**:把自然语言准确翻译为关系或逻辑表达式。
|
||
3. **选择分支结构**:单选、二选一、多选一、多选多、嵌套或 `switch`。
|
||
4. **实现各分支代码**:在每个分支写入对应处理代码。
|
||
|
||
### 案例一:BMI 分类
|
||
|
||
输入体重 `mass`(kg)和身高 `height`(m),计算 BMI。小于 18.5 输出 `Underweight`;小于 24 输出 `Normal`;否则输出 `Overweight`。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
using namespace std;
|
||
|
||
int main() {
|
||
double mass, height;
|
||
cin >> mass >> height;
|
||
|
||
double bmi = mass / (height * height);
|
||
|
||
if (bmi < 18.5) {
|
||
cout << "Underweight";
|
||
} else if (bmi < 24) {
|
||
cout << "Normal";
|
||
} else {
|
||
cout << "Overweight";
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
第二个分支隐含了第一个条件不成立,即 `bmi >= 18.5`,所以只需继续判断 `bmi < 24`。
|
||
|
||
### 案例二:闰年判断
|
||
|
||
输入年份。如果是闰年,输出该年份;否则输出 `No`。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
using namespace std;
|
||
|
||
int main() {
|
||
int year;
|
||
cin >> year;
|
||
|
||
bool leap = ((year % 4 == 0) && (year % 100 != 0))
|
||
|| (year % 400 == 0);
|
||
|
||
if (leap) {
|
||
cout << year;
|
||
} else {
|
||
cout << "No";
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
本题先用关系和逻辑运算构建一个完整条件,再用 `if-else` 根据真假输出。
|
||
|
||
### 案例三:简易计算器
|
||
|
||
输入形如 `7 * 2` 的表达式,根据运算符完成计算。
|
||
|
||
```cpp
|
||
#include <iostream>
|
||
using namespace std;
|
||
|
||
int main() {
|
||
double a, b;
|
||
char op;
|
||
cin >> a >> op >> b;
|
||
|
||
switch (op) {
|
||
case '+':
|
||
cout << a + b;
|
||
break;
|
||
case '-':
|
||
cout << a - b;
|
||
break;
|
||
case '*':
|
||
cout << a * b;
|
||
break;
|
||
case '/':
|
||
if (b == 0) {
|
||
cout << "Error";
|
||
} else {
|
||
cout << a / b;
|
||
}
|
||
break;
|
||
default:
|
||
cout << "Unknown operator";
|
||
break;
|
||
}
|
||
|
||
return 0;
|
||
}
|
||
```
|
||
|
||
`switch` 负责对字符 `op` 做等值匹配;除法分支中再用嵌套 `if` 检查除数是否为 0。
|
||
|
||
## 本章练习
|
||
|
||
### 一、表达式练习
|
||
|
||
用 C++ 表达式表示下列条件:
|
||
|
||
1. `x` 大于等于 10 且小于等于 20。
|
||
|
||
<br>
|
||
|
||
2. `x` 小于 0 或大于 100。
|
||
|
||
<br>
|
||
|
||
3. `n` 是 5 的倍数。
|
||
|
||
<br>
|
||
|
||
4. `n` 能被 3 整除,但不能被 2 整除。
|
||
|
||
<br>
|
||
|
||
5. 字符 `ch` 是大写英文字母。
|
||
|
||
<br>
|
||
|
||
6. 年龄 `age` 不在 7~15 岁的范围内。
|
||
|
||
<br>
|
||
|
||
7. 数学或英语至少有一门达到 90 分,并且作业已经完成。变量分别为 `math`、`english`、`homeworkFinished`。
|
||
|
||
<br>
|
||
|
||
9. `a`、`b`、`c` 三个条件中至少有两个为真。
|
||
|
||
<br>
|
||
|
||
### 二、代码阅读
|
||
|
||
1. 写出程序的输出结果:
|
||
|
||
```cpp
|
||
int x = 8;
|
||
if (x > 5) {
|
||
cout << "A";
|
||
}
|
||
if (x % 2 == 0) {
|
||
cout << "B";
|
||
}
|
||
```
|
||
|
||
<br><br>
|
||
|
||
2. 写出程序的输出结果,并说明原因:
|
||
|
||
```cpp
|
||
int score = 85;
|
||
if (score >= 60) {
|
||
cout << "及格";
|
||
} else if (score >= 80) {
|
||
cout << "良好";
|
||
} else if (score >= 90) {
|
||
cout << "优秀";
|
||
}
|
||
```
|
||
|
||
<br><br>
|
||
|
||
3. 写出 `y` 的最终值:
|
||
|
||
```cpp
|
||
int x = 5;
|
||
int y = 10;
|
||
bool result = (x > 1) || (++y > 20);
|
||
```
|
||
|
||
<br><br>
|
||
|
||
4. 写出程序的输出结果:
|
||
|
||
```cpp
|
||
int n = 2;
|
||
switch (n) {
|
||
case 1:
|
||
cout << "A";
|
||
case 2:
|
||
cout << "B";
|
||
case 3:
|
||
cout << "C";
|
||
break;
|
||
default:
|
||
cout << "D";
|
||
}
|
||
```
|
||
|
||
<br><br>
|
||
|
||
5. 写出程序的输出结果:
|
||
|
||
```cpp
|
||
bool loggedIn = true;
|
||
bool isAdmin = false;
|
||
|
||
if (loggedIn) {
|
||
if (isAdmin) {
|
||
cout << "管理员";
|
||
} else {
|
||
cout << "普通用户";
|
||
}
|
||
} else {
|
||
cout << "未登录";
|
||
}
|
||
```
|
||
|
||
<br><br>
|
||
|
||
6. 判断表达式 `5 < x < 10` 能否正确表示 `x` 在 5 和 10 之间,并说明计算过程。
|
||
|
||
<br><br>
|
||
|
||
### 三、程序改错
|
||
|
||
1. 改正下面的判断语句:
|
||
|
||
```cpp
|
||
if (score = 100); {
|
||
cout << "Full marks";
|
||
}
|
||
```
|
||
|
||
2. 下面的程序想判断 `x` 是否在闭区间 [10, 20] 内,请改正:
|
||
|
||
```cpp
|
||
if (10 <= x <= 20) {
|
||
cout << "Yes";
|
||
}
|
||
```
|
||
|
||
3. 下面的成绩分级程序可以正确的区分等级吗?
|
||
|
||
```cpp
|
||
if (score >= 60) {
|
||
cout << "C";
|
||
} else if (score >= 80) {
|
||
cout << "B";
|
||
} else if (score >= 90) {
|
||
cout << "A";
|
||
} else {
|
||
cout << "D";
|
||
}
|
||
```
|
||
|
||
|
||
### 四、程序设计
|
||
|
||
1. **奇偶判断**
|
||
|
||
输入一个整数,偶数输出 `Even`,奇数输出 `Odd`。
|
||
|
||
2. **景点门票**
|
||
|
||
某景点全价票 50 元。年龄不超过 6 岁或不低于 60 岁时票价为 25 元,其余情况为 50 元。输入年龄,输出票价。
|
||
|
||
- 样例输入:`65`
|
||
- 样例输出:`25`
|
||
|
||
3. **三个数的最大值**
|
||
|
||
输入三个整数,使用分支语句输出其中的最大值。
|
||
|
||
- 样例输入:`6 5 2`
|
||
- 样例输出:`6`
|
||
|