本章内容
ASCII 与类型转换
分支与循环结构进阶
让思绪在逻辑的琴弦上跳跃,用创意点亮无垠的编程星空,你的旅程开始精彩。
一、ASCII 与类型转换
🖥️ 一、ASCII 编码
1. 什么是 ASCII
- • ASCII(American Standard Code for Information Interchange) 是一种字符编码标准,用于在计算机内部将字符映射为对应的整数值。
- • ASCII 码定义了 0~127 共 128 个字符的编码,包括:
-
- • 控制字符(0~31,+127,常用于通信控制,例如换行、回车等)
- • 可打印字符(32~126),涵盖空格、数字、英文字母、标点符号等。
2. 常用字符的 ASCII 范例
在二级考试大纲中,强调几个常见字符及其 ASCII 码值,考生需能熟练识别并进行相互转换:
- • 空格(
' '
)对应 ASCII 码 32 - • 数字字符
'0'
对应 ASCII 码 48 - • 大写字母
'A'
对应 ASCII 码 65 - • 小写字母
'a'
对应 ASCII 码 97
例如:
#include <bits/stdc++.h>
using namespace std;
int main() {
char ch1 = 'A';
char ch2 = 'a';
char ch3 = '0';
cout << int(ch1) << endl; // 输出 65
cout << int(ch2) << endl; // 输出 97
cout << int(ch3) << endl; // 输出 48
return 0;
}
上述代码通过将 char
强制转换为 int
,可以得到对应的 ASCII 数值。
3. ASCII 码与字符之间的相互转换
3.1 从字符到整数
- • 方法一:显式强制转换
将char
类型的变量直接转换为int
:
char c = 'Z';
int x = (int)c; // x = 90
- • 方法二:借助算术运算自动转换
在表达式中,char
会被提升(promotion)为对应的整数参与运算:
char c = '5';
int x = c - '0'; // '5' 的 ASCII 是 53,'0' 是 48,所以 x = 5
3.2 从整数到字符
- • 方法一:显式强制转换
如果已知一个整数是有效的 ASCII 值,则可以直接转换为char
:
int code = 66;
char c = (char)code; // c = 'B'
- • 方法二:通过字符算术运算
当需要将数字型结果转换为字符数字时,可用:
int digit = 7;
char c = '0' + digit; // c = '7'
3.3 典型应用场景
- • 判断一个字符是否为数字
char ch;
cin >> ch;
if ('0' <= ch && ch <= '9') {
// ch 是 '0'~'9' 范围内的数字字符
}
- • 将输入的数字字符转换为对应的整数值
char ch;
cin >> ch; // 假设输入 '8'
int val = ch - '0'; // val = 8
- • 将一个数值转换为对应的字符进行输出
int num = 4;
char ch = '0' + num; // ch = '4'
cout << ch << endl; // 输出 '4'
🔄 二、数据类型转换
GESP 二级要求考生不仅要了解基础类型定义,还要掌握类型转换──包括隐式类型转换与强制类型转换。
1. 隐式类型转换(Implicit Conversion 或自动类型提升)
1.1 定义
- • 当不同数据类型在同一表达式中混合使用时,C++ 编译器会按照一定的“类型提升规则”自动将低精度类型转换成高精度类型,以保证运算的正确与精度。例如:
int + double
会将int
提升为double
后再做浮点运算。
1.2 规则概述
- 1. 算术转换顺序(由低到高):
char
→short
→int
→long
→long long
→float
→double
→long double
- 2. 布尔类型(
bool
)转换为其他数值类型时,false
转为0
,true
转为1
。 - 3. 整型与浮点混合运算:若有浮点参与,则整型会被提升为相应的浮点类型。
- 4. 字符型与整型:
char
、unsigned char
、signed char
等会被提升为int
(若char
范围在int
能覆盖的范围内,否则提升为unsigned int
)。
1.3 示例说明
#include <bits/stdc++.h>
using namespace std;
int main() {
char c = '3'; // ASCII 码 51
int a = 5;
double d = 2.5;
// 示例 1:char 与 int 运算
int result1 = c + a; // c 会先提升为 int(51),51 + 5 = 56
// result1 == 56
// 示例 2:int 与 double 运算
double result2 = a + d; // a 提升为 5.0,5.0 + 2.5 = 7.5
// result2 == 7.5
// 示例 3:混合多种类型
float f = 1.2f;
double result3 = c + a + f + d;
// c → 51(int) → 51.0(double), a → 5.0(double), f → 1.2(double)
// 最终运算为 double 类型:51.0 + 5.0 + 1.2 + 2.5 = 59.7
// result3 == 59.7
return 0;
}
在以上示例中,看出在表达式中各类型会“自动”转换,以保证整体运算无误且尽可能保留精度。
1.4 隐式转换带来的常见陷阱
- • 整数除法与浮点运算
int x = 5, y = 2;
double r = x / y; // x/y 都是 int,会进行整数除法:5/2 = 2
// 所以 r 最终得到 2.0,而不是 2.5
double r2 = x / (double)y; // 通过显式将 y 转为 double,5/(double)2 = 2.5
- • 布尔类型转换
bool b = false;
int i = b + 5; // b 自动转换为 0,i = 0 + 5 = 5
- •
char
和signed char
的符号位差异
在不同编译器下,char
默认可为有符号或无符号,若char
为有符号,则其范围为 -128~127,否则为 0~255,可能导致在与int
运算时结果不同。
char c = 200; // 若 char 为有符号,200 将溢出转换为 -56(200-256=-56)
unsigned char uc = 200; // 无符号 char,范围 0~255,保持 200
int a1 = c + 0; // 若 c 为 -56,则 a1 = -56
int a2 = uc + 0; // a2 = 200
🛠️ 三、强制类型转换(Explicit Conversion 或 C 风格/函数式转换)
3.1 定义
- • 强制类型转换即由程序员显式地告诉编译器将某一数据类型的值转换为另一类型,以便于满足某些特殊运算需求或避免编译器警告。
- • 在 C++ 中常用的写法包括:
-
- 1. C 风格转换:
(目标类型)表达式
- 2. 函数式转换:
目标类型(表达式)
- 3. C++-style 转换运算符:推荐使用四种运算符
static_cast<目标类型>(表达式)
、const_cast<目标类型>(表达式)
、reinterpret_cast<目标类型>(表达式)
和dynamic_cast<目标类型>(表达式)
,其中最常用的是static_cast
。
- 1. C 风格转换:
3.2 常见场景与示例
3.2.1 将浮点数转换为整数
- • 向下截断:将浮点类型转换为整型时,小数部分会被舍弃(截断):
double pi = 3.14159;
int n = (int)pi; // C 风格转换,n = 3
int m = static_cast<int>(pi); // C++ 风格转换,m = 3
注意:若浮点数很大而超出整型范围,将会发生未定义行为或溢出。
3.2.2 将整型转换为浮点型
- • 保留小数计算:必须显式转换,否则会发生整数运算:
int a = 7, b = 2;
double r1 = (double)a / b; // 7.0/2 = 3.5
double r2 = a / b; // 先做整数除法 7/2 = 3,再转换为 3.0(非预期)
3.2.3 将字符转换为整型或整型转换为字符
- • 获取字符 ASCII 码:
char ch = 'X';
int code = (int)ch; // code = 88
int code2 = static_cast<int>(ch); // 同样得到 88
- • 将整数转换为字符(要求值在 0~255 范围内):
int num = 66;
char c = static_cast<char>(num); // c = 'B'
3.2.4 指针类型之间的转换
- • 这种转换不在二级范围内,此处仅提及:
int* pInt;
void* pVoid = static_cast<void*>(pInt); // 允许整型指针转换为 void 指针
3.3 强制转换的注意事项
- 1. 可能丢失精度:将
double
转int
时,直接截断小数部分;或long long
转int
时,若超出目标类型范围会溢出。 - 2. 非法的强制转换:若尝试将不兼容类型强制转换(如将
string
转为int
),会导致编译错误或未定义行为。 - 3. 优先使用 C++ 样式转换:如
static_cast<>()
,可以在编译阶段做更多检查,体现语义更清晰,也降低出错概率。 - 4. 谨慎使用
reinterpret_cast
:此运算符会绕过类型系统,通常不在基础阶段使用。
📋 四、综合示例与常见题型
下述示例均为二级考试中可能出现的考题类型,通过练习可加深对 ASCII 与类型转换的理解。
示例 1:判断输入字符类型并输出 ASCII
#include <bits/stdc++.h>
using namespace std;
int main() {
char ch;
cin >> ch; // 输入一个任意字符
cout << "字符 " << ch << " 的 ASCII 码为 " << int(ch) << endl;
if ('0' <= ch && ch <= '9') {
cout << "这是一个数字字符,其对应整数值为 " << ch - '0' << endl;
} else if ('A' <= ch && ch <= 'Z') {
cout << "这是一个大写字母" << endl;
} else if ('a' <= ch && ch <= 'z') {
cout << "这是一个小写字母" << endl;
} else {
cout << "这是一个其他字符" << endl;
}
return 0;
}
- • 要点:
-
- 1. 通过
int(ch)
获得其 ASCII 码; - 2. 利用区间比较判断字符类别;
- 3. 若为数字字符,则再减去
'0'
转为相应整数。
- 1. 通过
示例 2:不同类型混合运算下的隐式转换
#include <bits/stdc++.h>
using namespace std;
int main() {
int a = 5;
double b = 2.0;
char c = '3'; // ASCII 51
// 隐式转换示例
double res1 = a + b; // a 提升为 double:5.0 + 2.0 = 7.0
int res2 = a + c; // c 提升为 int(51):5 + 51 = 56
double res3 = a + (c - '0'); // c - '0' = 51 - 48 = 3, 再与 a 相加:5 + 3 = 8.0
cout << res1 << " " << res2 << " " << res3 << endl;
return 0;
}
- • 要点:
-
- 1.
a + b
中a
自动提升为double
; - 2.
a + c
中c
自动提升为int
; - 3.
(c - '0')
用于获取字符数字对应的数值。
- 1.
示例 3:强制类型转换解决数据丢失
#include <bits/stdc++.h>
using namespace std;
int main() {
int x = 7, y = 2;
double z1 = x / y; // 整数除法先执行,结果为 3,再转 double → 3.0
double z2 = static_cast<double>(x) / y; // 强制将 x 转为 double:7.0/2 = 3.5
cout << "z1 = " << z1 << endl; // 输出 3.0
cout << "z2 = " << z2 << endl; // 输出 3.5
// 将大写字母转换为小写字母
char ch = 'G'; // ASCII 71
char lower = static_cast<char>(ch + 32); // 71 + 32 = 103,对应 'g'
cout << lower << endl;
return 0;
}
- • 要点:
-
- 1. 若不强制转换,
x / y
会先进行整数运算; - 2. 强制将
x
转double
,才能得到正确的小数结果; - 3. 字母大小写转换可利用 ASCII 码差值(大写→小写相差 32)。
- 1. 若不强制转换,
❓ 五、常见考题方向
- 1. 单选/判断题
-
- • 判断代码段中隐式转换结果是否符合预期,例如:
int a = 5;
char c = '8'; // ASCII 56
int r = a + c; // r = 5 + 56 = 61
-
- • 判断整数、字符与浮点数之间运算后的类型与数值结果;
- • 判断某段强制转换后,是否存在精度丢失或溢出风险;
- • 判断流程图中若用 ASCII 值做比较时,逻辑分支是否正确。
- 2. 编程题
-
- • 示例题目:某程序依次读入两个字符
c1
和c2
,要求判断它们是否为同一类字符(均为数字、均为大写字母、均为小写字母或其他),并输出对应描述。此题需要综合掌握 ASCII 区间判断与字符转换。 - • 示例题目:给定一个字符串,统计其中数字字符出现的总和,例如
"a1b2c3"
输出 6,此题需将每个数字字符转换为对应整数后累加。
- • 示例题目:某程序依次读入两个字符
📌 六、总结与提示
- 1. 牢牢记住常用字符 ASCII 码:空格 32、数字
'0'
48、大写'A'
65、小写'a'
97。 - 2. 区分隐式与强制转换:
-
- • 隐式转换 看运算场景,编译器自动完成;
- • 强制转换 由程序员显式书写,需谨慎避免精度丢失。
- 3. 注意整数除法与浮点运算差异,考试中常见陷阱。
- 4. 使用 C++ 推荐的
static_cast
进行类型转换,语义更明确,易读易维护。 - 5. 练习时多列举边界情况(如负数、极大值、不同
char
有符号的差异等),保证应对各类题型。
🎯 二、分支与循环结构进阶
以下内容基于《GESP 考级大纲》与历年二级真题,深入讲解如何合理设计和使用 嵌套 if-else
与 嵌套循环,包括经典模式、常见陷阱与示例解析。所有示例均使用 C++(#include <bits/stdc++.h>
),帮助考生在复杂条件与多重循环场景中保持逻辑清晰、代码高效。
🧩 一、嵌套 if-else
结构
1. 基本概念
- 1. 单层
if-else
if (条件1) {
// 语句块 A
}
else {
// 语句块 B
}
-
- • 若“条件1”成立,则执行 “语句块 A”,否则执行 “语句块 B”。
- 2. 嵌套
if-else
if (条件1) {
if (条件2) {
// 语句块 A1
}
else {
// 语句块 A2
}
}
else {
// 语句块 B
}
-
- • 在外层条件成立的前提下,再进一步判断“条件2”,形成多级分支。
2. 嵌套层次与逻辑清晰化
- • 最深嵌套:理论上,可以任意多层嵌套,但为了可读性建议不超过 3 层。
- • 逻辑梳理技巧:在编写嵌套时,可先用伪代码或流程图理清分支,避免“迷宫式”代码:
-
- 1. 明确每层“条件”之间的逻辑关系(与、或、互斥等);
- 2. 对“共同行为”提取到外层,避免重复;
- 3. 使用早期
return
(或break
/continue
)跳出,减少嵌套深度。
3. 常见模式与示例
3.1 二级分支 + 三级分支示例
题目背景:判断一个年份 year
是否为闰年,并根据该年份所在的季度输出一条信息。
- • 闰年判断规则:
-
- 1. 如果
year % 400 == 0
,则为闰年; - 2. 否则若
year % 4 == 0 && year % 100 != 0
,则为闰年; - 3. 其余均非闰年。
- 1. 如果
- • 季度判断:
-
- •
1 ≤ 月 ≤ 3
→ 第一季度 - •
4 ≤ 月 ≤ 6
→ 第二季度 - •
7 ≤ 月 ≤ 9
→ 第三季度 - •
10 ≤ 月 ≤ 12
→ 第四季度
- •
#include <bits/stdc++.h>
using namespace std;
int main() {
int year, month;
cin >> year >> month; // 假设输入:2024 5
// 外层:判断是否闰年
if ( (year % 400 == 0) || (year % 4 == 0 && year % 100 != 0) ) {
cout << year << " 是闰年,";
// 内层:判断季度
if (month >= 1 && month <= 3) {
cout << "属于第一季度" << endl; // 不会走这里
}
else if (month >= 4 && month <= 6) {
cout << "属于第二季度" << endl; // 2024 5 → 属于第二季度
}
else if (month >= 7 && month <= 9) {
cout << "属于第三季度" << endl;
}
else if (month >= 10 && month <= 12) {
cout << "属于第四季度" << endl;
}
else {
cout << "月份不合法" << endl;
}
}
else {
cout << year << " 不是闰年,";
// 可以复用相同季度判断逻辑,示例中直接输出
cout << "不进行季度判断" << endl;
}
return 0;
}
- • 解析:
-
- 1. 外层
if
先判断“闰年”或“平年”; - 2. 若是闰年,再嵌套一个
if-else if-...
判断“月份”对应的季度; - 3. 若平年,无需再判断季度。
- 1. 外层
- • 要点:合理区分“外部条件”与“内部条件”,保持分支含义清晰。
3.2 多条件互斥 + 嵌套分支
题目背景:某商店针对不同会员等级和消费金额实行折扣:
- • 会员等级:“普通(1)”、“银卡(2)”、“金卡(3)”。
- • 消费金额分段:
-
- •
< 100
→ 无折扣 - •
100 ≤ x < 500
→ 9 折 - •
x ≥ 500
→ 8 折
- •
- • 金卡会员还有额外 5% 优惠。
#include <bits/stdc++.h>
using namespace std;
int main() {
int level; // 1—普通,2—银卡,3—金卡
double amount; // 消费金额
cin >> level >> amount;
double pay = amount; // 最终支付金额
// 先按消费金额分段
if (amount < 100) {
if (level == 3) {
// 金卡但金额 < 100,无基础折扣,但仅金卡再打 95 折
pay *= 0.95;
}
// 其他等级,无折扣
}
else if (amount < 500) {
// 9 折
pay *= 0.9;
if (level == 3) {
// 金卡再额外 95 折
pay *= 0.95;
}
}
else {
// amount ≥ 500,8 折
pay *= 0.8;
if (level == 3) {
// 金卡再额外 95 折
pay *= 0.95;
}
}
cout << fixed << setprecision(2);
cout << "最终需支付:" << pay << endl;
return 0;
}
- • 解析:
-
- 1. 外层
if-else if-else
根据“消费金额”分为三段; - 2. 每个金额段中,再判断
level == 3
(金卡)是否享受额外 5% 优惠; - 3. 这样能保证逻辑先按金额折扣,再按会员等级执行二级折扣。
- 1. 外层
- • 要点:
-
- • 嵌套分支避免重复代码,将“金卡额外优惠”逻辑写入每个金额分段内部;
- • 如果“金卡优惠”对所有金额段都相同,可考虑先判断会员级别再判断金额,以减少嵌套深度。
4. 常见陷阱与优化
- 1. 嵌套过深导致可读性差
-
- • 若发现三层以上嵌套,可考虑拆分子函数/子流程,或使用“守卫式”写法(Guard Clause)减少嵌套:
if (!外层条件) {
// 直接处理不符合情况
return;
}
// 剩余逻辑放在外层,无需嵌套 else
if (!内层条件) {
// 处理另一种情况
return;
}
// 继续执行后续操作
- 2. 多重
else if
互斥判断顺序
-
- •
else if
链必须按照从最小可能性到最大可能性,或按照题意所需的优先级排列,否则会走错分支。
- •
- 3. 严格判断边界
-
- • 特别是数值区间判断(如
>=
、<
),要保证各分支覆盖所有可能,不留“黑洞”或重复。
- • 特别是数值区间判断(如
🔄 二、嵌套循环结构
1. 基本概念
- 1. 单层循环
for (int i = 0; i < N; i++) {
// 循环体
}
- 2. 嵌套循环
for (int i = 0; i < N; i++) {
for (int j = 0; j < M; j++) {
// 内层循环体
}
// 外层循环后续操作
}
-
- • 外层循环 每迭代一次,都会“启动”一次完整的内层循环。
- • 嵌套循环常用于“二维遍历”、“组合枚举”等场景。
2. 常见模式与示例
2.1 二重循环:矩阵输入/输出与求和
示例 1:读入一个 n x m
矩阵并输出其元素总和
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m; // 假设 n=3, m=4
vector<vector<int>> a(n, vector<int>(m)); // 也可以用 int a[n][m] 全局变量申请
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
}
}
long long sum = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
sum += a[i][j];
}
}
cout << "矩阵元素之和:" << sum << endl;
return 0;
}
- • 解析:
-
- • 第一层
for
负责行i
,第二层for
负责列j
; - • 内层循环完成一次“行遍历”,外层循环继续到下一行。
- • 第一层
2.2 多重嵌套:阶乘表与排列表
示例 2:输出 1!
到 N!
的表格
#include <bits/stdc++.h>
using namespace std;
int main() {
int N;
cin >> N; // 假设 N=5
// 输出表头
cout << setw(4) << "k" << setw(8) << "k!" << endl;
// 计算并输出
for (int k = 1; k <= N; k++) {
long long fact = 1;
for (int i = 1; i <= k; i++) {
fact *= i;
}
cout << setw(4) << k << setw(8) << fact << endl;
}
return 0;
}
- • 解析:
-
- • 外层
for (k = 1…N)
枚举要计算的阶乘基数; - • 内层
for (i = 1…k)
通过乘法累积完成k!
计算; - • 每次计算结束后输出一行,并继续下一
k
。
- • 外层
示例 3:打印 1~`N 的所有 3 元组排列(
i < j < k`)
#include <bits/stdc++.h>
using namespace std;
int main() {
int N;
cin >> N; // 假设 N=4
// 三重循环枚举 i, j, k,使得 i < j < k
for (int i = 1; i <= N; i++) {
for (int j = i + 1; j <= N; j++) {
for (int k = j + 1; k <= N; k++) {
cout << "(" << i << "," << j << "," << k << ") ";
}
cout << endl;
}
}
return 0;
}
- • 解析:
-
- • 第一层枚举
i
,第二层从i+1
开始枚举j
,第三层从j+1
开始枚举k
,保证i < j < k
; - • 此模式常用于“组合”或“三元组”枚举题。
- • 第一层枚举
2.3 嵌套循环结合条件判断
示例 4:最好的草
- • 题意: 在牧场地图中,每个草丛要么是单个“#”,要么是有公共边的相邻两个“#”。给定牧场地图,计算有多少个草丛(数据都比较小可以用二维数组申请)。:
#include<bits/stdc++.h>
using namespace std;
int n, m, sum;
char a[101][101];
int main(){
cin >> n >> m;
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
cin >> a[i][j];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(a[i][j] == '#'){
if(a[i+1][j] == '#'){
a[i+1][j] = '.';
}
else if(a[i][j+1] == '#'){
a[i][j+1] = '.';
}
}
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= m; j++){
if(a[i][j] == '#'){
sum++;
}
}
}
cout << sum << endl;
return 0;
}
- • 解析:
-
- • 最外层双重循环遍历矩阵每个位置;
- • 如果当前为
#
且未访问,则对该#
启动 草堆搜查(下和右)。
3. 嵌套循环的时间复杂度
- 1. 双重循环
-
- • 两层
for (i=0…n) { for (j=0…m) {…} }
时间复杂度为O(n × m)
。 - • 当
m = n
时,即O(n²)
。
- • 两层
- 2. 三重及以上循环
-
- • 三层
for (i=0…n) { for (j=0…n) { for (k=0…n) {…} } }
时间复杂度为O(n³)
。 - • 多层嵌套很容易导致“指数级”或“多项式级”增长,需要谨慎使用,注意题目限制。
- • 三层
- 3. 结合条件判断提前跳出
-
- • 在循环内部遇到满足条件的结果后,可以通过
break
、continue
或return
尽早跳出,降低平均执行量。 - • 例如在“三元组枚举”时,当某些条件过早不满足,可直接
break
内层循环。
- • 在循环内部遇到满足条件的结果后,可以通过
4. 嵌套循环常见陷阱
- 1. 内外层变量范围冲突
for (int i = 0; i < n; i++) {
for (int i = 0; i < m; i++) { // 错误:内层重新使用 i,导致逻辑混乱
// …
}
}
-
- • 修正:内层循环变量与外层不同名(如
j
)。
- • 修正:内层循环变量与外层不同名(如
- 2. 循环边界 off-by-one(“一减” 或 “一增” 错误)
-
- • 写成
for (int i = 0; i <= n; i++)
但实际只需i < n
,导致访问越界。 - • 建议常用
i < n
、j < m
习惯写法,减少判断错误。
- • 写成
- 3. 过深嵌套导致性能瓶颈
-
- • 在
n ≤ 1e5
,不能使用O(n²)
;在n ≤ 5000
,要谨慎使用O(n³)
。 - • 考试时务必先看清题目约束,避免直接套用三重循环。
- • 在
🔀 三、分支与循环混合使用
1. 经典题型示例
1.1 判断“水仙花数”(Narcissistic Number)
- • 题意:输出所有 3 位数中等于其各位数字立方和的数。
- • 要点:
-
- • 利用三重循环可以简单粗暴地枚举
100
~999
,再拆分数字。
- • 利用三重循环可以简单粗暴地枚举
#include <bits/stdc++.h>
using namespace std;
int main() {
for (int num = 100; num <= 999; num++) {
int a = num / 100; // 百位
int b = (num / 10) % 10; // 十位
int c = num % 10; // 个位
// 判断是否为水仙花数
if (a*a*a + b*b*b + c*c*c == num) {
cout << num << " ";
}
}
cout << endl;
return 0;
}
- • 解析:
-
- • 单层循环枚举所有 3 位数;
- • 在循环体内部使用
if
判断其“立方和”条件。
1.2 打印九九乘法表
- • 题意:打印传统的
1×1=1
到9×9=81
,每行格式为i×j=ij
,且只打印j ≤ i
的项。 - • 要点:
-
- • 双重嵌套
for
,并在内层通过if (j ≤ i)
控制输出格式。
- • 双重嵌套
#include <bits/stdc++.h>
using namespace std;
int main() {
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= 9; j++) {
if (j <= i) {
cout << j << "×" << i << "=" << i * j;
if (j < i) cout << "\t";
}
}
cout << endl;
}
return 0;
}
- • 解析:
-
- • 外层
i
枚举乘数行号; - • 内层
j
枚举被乘数,当j > i
时,通过if
跳过输出。
- • 外层
2. 混合实例演练
2.1 等差数列二维数组填充
- • 题意:给定正整数
n
,生成一个n × n
的二维数组mat
,要求:
-
- • 主对角线为 1
- • 其余位置用与行下标成等差递增的数填充,例如
mat[i][j] = i + j + 1
(i,j
从 0 开始)
- • 要点:
-
- • 双重循环填充,并在循环体内用
if (i == j)
处理对角线。
- • 双重循环填充,并在循环体内用
#include <bits/stdc++.h>
using namespace std;
int main() {
int n;
cin >> n; // 假设 n=4
vector<vector<int>> mat(n, vector<int>(n));
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (i == j) {
mat[i][j] = 1;
} else {
mat[i][j] = i + j + 1;
}
}
}
// 输出矩阵
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
cout << setw(4) << mat[i][j];
}
cout << endl;
}
return 0;
}
- • 解析:
-
- • 内层
if (i == j)
分支确保对角线为 1; - • 否则按等差公式
i + j + 1
填充。
- • 内层
2.2 查找矩阵中特定元素
- • 题意:在一个升序排列的
n × m
矩阵中,每行和每列都从小到大排列。要求输入一个目标值target
,判断其是否存在于矩阵中。 - • 要点:
-
- • 不要直接用三重嵌套遍历所有元素;可以利用“行列有序”特性,每行内用二分或从右上角开始“走斜线”搜索。此处演示“三重循环 + 条件”思路(朴素版,效率较低,仅用作示意):
#include <bits/stdc++.h>
using namespace std;
int main() {
int n, m;
cin >> n >> m; // 假设 n=3, m=4
vector<vector<int>> mat(n, vector<int>(m));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> mat[i][j];
}
}
int target;
cin >> target;
bool found = false;
for (int i = 0; i < n && !found; i++) {
for (int j = 0; j < m && !found; j++) {
if (mat[i][j] == target) {
found = true;
}
}
}
if (found) cout << "存在" << endl;
else cout << "不存在" << endl;
return 0;
}
- • 解析:
-
- • 外层双重循环遍历每个元素,遇到相等立即置
found = true
并跳出两层循环; - • 可通过
&& !found
控制内层/外层提前终止,避免无谓遍历。
- • 外层双重循环遍历每个元素,遇到相等立即置
🔍 四、进阶技巧与陷阱
1. 提前跳出与标记变量
- • 在多重循环中,当满足目标时可用:
-
- 1.
break
只跳出当前循环层; - 2. 配合标记变量
bool flag
并在外层条件检查,或使用goto
(不推荐,但可实现多层跳出)。
- 1.
- • 示例:两层循环中找到第一个负数并跳出:
bool found = false;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (a[i][j] < 0) {
found = true;
break; // 退出内层循环
}
}
if (found) break; // 退出外层循环
}
2. 减少嵌套深度的常用策略
- 1. 功能函数拆分
bool rowAllPositive(const vector<int>& row) {
for (int x : row) {
if (x <= 0) return false;
}
return true;
}
int main() {
// ...
for (int i = 0; i < n; i++) {
if (rowAllPositive(mat[i])) {
// 处理这一行
}
}
return 0;
}
-
- • 将内部复杂逻辑(如“某行是否满足条件”)写成一个函数,外层循环只负责调用该函数,从而减少局部嵌套。
- 2. 早期返回(Guard Clause)
for (int i = 0; i < n; i++) {
if (mat[i].empty()) continue; // 过滤空行
// 正常处理逻辑,无需再嵌套 else
// ...
}
-
- • 当遇到“无效输入”或“不可行情况”时,立即
return
/continue
,把主要逻辑放在外层,减少层级。
- • 当遇到“无效输入”或“不可行情况”时,立即
- 3. 利用标准库算法
// 判断某行是否全为正数
if (all_of(mat[i].begin(), mat[i].end(), [](int x){ return x > 0; })) {
// 处理该行
}
-
- • C++ 标准库(如
<algorithm>
中的find_if
、any_of
、all_of
等)可将“循环 + 条件”替换为一行调用,提高可读性。
- • C++ 标准库(如
3. 小心无限循环
- • 常见写法错误:
for (int i = 0; i < n; i--) { … } // i 递减,导致无限循环
while (true) {
if (condition == false) ; // 注意分号陷阱,语句什么也不做
// 这里永远无法跳出
}
- • 防范策略:
-
- 1. 检查循环变量的更新(自增/自减)是否朝正确方向。
- 2.
while
或for(;;)
必须在循环体内包含可终止条件,否则要格外小心使用。
✅ 五、综合练习题(真题选编)
- 1. 题目 1(24年3月·二级真题改编)
-
- • 输入两个整数
a
、b
、c
,判断哪一个最大,如果其中有两个或三个相等且最大,则输出“相等”;否则输出最大值。 - • 要求使用嵌套
if-else
实现。
- • 输入两个整数
- 2. 题目 2(23年6月·二级真题改编)
-
- • 输入一个正整数
N
,输出如下格式的数字三角(右对齐):
- • 输入一个正整数
1
2 2
3 3 3
……
N N N … N
-
- • 要求使用嵌套循环与条件来控制空格与数字的输出。
- 3. 题目 3(24年6月·二级真题改编)
-
- • 输入一个
n × m
矩阵,统计并输出矩阵中所有行元素之和大于 100 的行数。 - • 要求使用双重循环与
if
判断,跳过不符合条件的行。
- • 输入一个
- 4. 题目 4(23年9月·二级真题改编)
-
- • 输入一个正整数
n
,判断1 ~ n
中有多少个数各位数字之和能被 3 整除。 - • 要点:一层循环枚举
i
,在循环内部嵌套拆分数字与求和、if (sum % 3 == 0)
判断。
- • 输入一个正整数
- 5. 题目 5(25年3月·二级真题改编)
-
- • 输入一个由若干整数构成的列表,依次判断列表中相邻三个数是否构成递增序列(如
a < b < c
),输出所有满足条件的三元组下标。 - • 要点:单层循环中利用
if (i+2 < len && a[i] < a[i+1] && a[i+1] < a[i+2])
判断。
- • 输入一个由若干整数构成的列表,依次判断列表中相邻三个数是否构成递增序列(如
🔖 六、总结
- • 嵌套
if-else
-
- • 确保每一层的条件独立且逻辑清晰;
- • 尽量避免超过 3 层嵌套,可通过“早期返回”、“子函数”进行重构;
- • 小心边界判断顺序,防止遗漏或覆盖错误。
- • 嵌套循环
-
- • 明确“外层”与“内层”的角色,避免变量命名冲突;
- • 关注时间复杂度:双层
O(n×m)
,三层O(n³)
,在大规模数据时需慎用; - • 勤用 “标记变量”、“
break
+标记” 或 “标准库算法” 优化,减少不必要的迭代。