前置知识:
一本通索引:https://gitee.com/wyloving/YiBenTongCode.git
深入探索for循环的进阶用法
在 C++ 中,for
循环是非常灵活的,它支持多种变种和用法,允许你省略其中的某些部分,甚至使用无限循环、反向循环等不同的结构。这种灵活性使得 for
循环在多种场景下都能有效使用。下面我将通过一些具体的示例来讲解 for
循环的不同变种用法。
基本的 for
循环语法
for (初始化表达式; 判断表达式; 增量表达式) {
// 循环体
}
在每次迭代时,执行顺序如下:
- 初始化表达式:仅在第一次迭代之前执行一次,通常用来设置循环变量。
- 判断表达式:每次迭代之前执行,决定是否继续执行循环体。
- 增量表达式:每次迭代后执行,通常用来更新循环变量。
1. 省略循环条件表达式
你可以省略循环的判断条件表达式,创建一个无限循环。在这种情况下,循环会一直进行,直到程序内部通过 break
或其他条件来终止它。
#include <iostream>
int main() {
int count = 0;
// 无限循环
for (; ; ) {
std::cout << "Loop iteration: " << count << std::endl;
count++;
// 当 count 等于 5 时退出循环
if (count >= 5) {
break;
}
}
return 0;
}
2. 省略初始化表达式
在 for
循环中,你可以省略初始化部分,循环变量可以在循环外部定义或初始化。这样允许你在循环外部进行更复杂的初始化。
#include <iostream>
int main() {
int count = 0;
// 省略初始化部分,直接使用外部定义的 count
for (; count < 5; count++) {
std::cout << "Loop iteration: " << count << std::endl;
}
return 0;
}
3. 省略增量表达式
for
循环的增量部分是可选的,如果你不需要增量表达式,可以将其省略。通过这种方式,你可以手动控制循环变量的变化。
#include <iostream>
int main() {
int count = 0;
// 省略增量部分
for (; count < 5; ) {
std::cout << "Loop iteration: " << count << std::endl;
count += 2; // 自定义增量
}
return 0;
}
在这个示例中,count
每次增加 2,而不是默认的 1。
4. 省略所有三个部分
for
循环的三个部分都可以省略,这样就可以让你手动控制循环的各个方面。例如,你可以将循环的控制完全交给 break
和 continue
语句。
#include <iostream>
int main() {
int count = 0;
// 省略所有三个部分
for (;;) {
std::cout << "Loop iteration: " << count << std::endl;
count++;
if (count == 3) {
std::cout << "Breaking at count = " << count << std::endl;
break; // 跳出循环
}
}
return 0;
}
5. 使用反向循环
for
循环也可以通过设置适当的初始值、判断条件和增量表达式来进行反向循环。比如,倒序打印一个数组的元素:
#include <iostream>
int main() {
int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);
// 反向循环
for (int i = n - 1; i >= 0; i--) {
std::cout << arr[i] << " ";
}
return 0;
}
6. 使用多个循环变量
for
循环的初始化、条件和增量部分都支持使用多个变量,并且你可以在这些部分中进行不同的操作。这对于复杂的循环结构非常有用。
#include <iostream>
int main() {
// 使用多个循环变量
for (int i = 0, j = 10; i < 5 && j > 5; i++, j -= 2) {
std::cout << "i = " << i << ", j = " << j << std::endl;
}
return 0;
}
7. 使用 for
循环遍历容器(C++11 及以上)
C++11 引入了基于范围的 for
循环,简化了数组或容器的遍历。这样你就可以不用显式地使用索引来遍历容器。
#include <iostream>
#include <vector>
int main() {
std::vector<int> vec = {1, 2, 3, 4, 5};
// 基于范围的 for 循环
for (int value : vec) {
std::cout << value << " ";
}
return 0;
}
8. 嵌套 for
循环
有时候,我们需要嵌套多个 for
循环来处理多维数据。比如,打印一个二维数组:
#include <iostream>
int main() {
int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
// 嵌套的 for 循环
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
std::cout << arr[i][j] << " ";
}
std::cout << std::endl;
}
return 0;
}
总结
C++ 中的 for
循环非常灵活,允许你根据需要省略其中的任何部分(初始化、条件判断、增量),甚至省略全部部分来控制循环的执行。你可以用它来处理常规的递增循环,也可以用它来实现倒序循环、嵌套循环等。通过灵活地应用这些变种,你可以让代码更加简洁和高效。
卫语句
“卫语句”(Guard Clauses)是编程中一种用于简化代码结构的常见设计模式,尤其是在处理多重条件判断时,它通过提前返回或退出函数,避免了过多的嵌套,从而使代码更加清晰和易于理解。
Guard Clauses 的概念
Guard Clause 的核心思想是,当某个条件不满足时,提前退出函数,避免后续的复杂嵌套。通过这种方式,减少了代码的层级,使得代码更加直观和简洁。
场景描述
假设我们要编写一个函数,根据学生的成绩来判断他们的评定等级。根据成绩的不同,给出以下评定:
- 成绩大于等于 90 分:优秀
- 成绩大于等于 75 分但小于 90 分:良
- 成绩大于等于 60 分但小于 75 分:合格
- 成绩小于 60 分:待及格
在不使用卫语句的情况下,代码可能会比较嵌套,像这样:
没有使用 Guard Clauses 的版本
#include <iostream>
void evaluateGrade(int score) {
if (score >= 90) {
std::cout << "优秀" << std::endl;
} else {
if (score >= 75) {
std::cout << "良" << std::endl;
} else {
if (score >= 60) {
std::cout << "合格" << std::endl;
} else {
std::cout << "待及格" << std::endl;
}
}
}
}
int main() {
int score = 82; // 你可以改变这个值来测试不同的成绩
evaluateGrade(score);
return 0;
}
代码分析
在上面的代码中,每次我们用 else
来判断下一个区间的成绩。这种多重嵌套使得代码层级较深,尤其当条件判断较多时,代码可读性和维护性都会受到影响。
使用 Guard Clauses 优化后的版本
我们可以应用“卫语句”(Guard Clauses)模式,提前处理掉不符合条件的情况,然后在剩下的条件中进行处理。这种方式能有效减少代码的层级,使得代码更加简洁、直观。
#include <iostream>
void evaluateGrade(int score) {
if (score >= 90) {
std::cout << "优秀" << std::endl;
return;
}
if (score >= 75) {
std::cout << "良" << std::endl;
return;
}
if (score >= 60) {
std::cout << "合格" << std::endl;
return;
}
std::cout << "待及格" << std::endl;
}
int main() {
int score = 82; // 你可以改变这个值来测试不同的成绩
evaluateGrade(score);
return 0;
}
代码分析
在这个版本中,我们通过使用 Guard Clauses 来简化代码:
- 每次判断一个条件时,如果条件满足,立即返回结果,跳出函数。
- 不再需要多个
else
语句来处理剩余条件。 - 逻辑非常清晰,从高到低依次进行判断,一旦条件满足,就结束函数,返回相应的评定。
优点总结
- 减少嵌套:通过提前返回,使得代码不再有层层嵌套的结构。每个判断条件都被处理完后立刻返回,使得代码的层级更浅,逻辑更清晰。
- 提高可读性:每个条件语句的意图更直观,避免了多重嵌套使代码变得难以理解。
- 简洁易维护:如果以后要更改成绩的评定条件,只需修改对应的 Guard Clause,而不会影响其他的代码逻辑。
通过这个简单的例子,你可以看到,卫语句(Guard Clauses)通过提前处理异常或边界情况,能够让代码更简洁、直观,也更易于维护。
c++ 内存结构
想一想:如果你是编译器,给你一段别人写的代码,你该怎么高度运行这段代码?
程序编程后生成可执行程序(exe)
二进制代码被调入“代码区域”(内存内)
这些内存区域在程序执行期间协同工作,每个区域有不同的生命周期、作用和管理方式。下面是对这些内存区块的关系和使用的详细说明:
代码区:
- 作用:存储程序的机器码指令,包括执行代码和只读数据。
- 使用:程序在启动时被加载到内存,指令和只读数据在代码区执行。
常量存储区:
作用:存储不可修改的常量数据,如字符串常量、全局常量、const修饰的变量等。
使用:常量数据在程序加载时被分配内存,通常以只读的方式存储。
全局/静态存储区:
- 作用:存储全局变量和静态变量,其生命周期贯穿整个程序执行过程。
- 使用:全局变量在程序启动时分配内存,静态变量在声明时分配内存。它们的数据在整个程序执行期间可读写。
栈:
- 作用:用于存储函数调用信息、局部变量、临时数据等。遵循后进先出(LIFO)原则。
- 使用:每个函数调用都会创建一个
栈帧
,包含局部变量和函数调用信息。栈帧在函数返回时被销毁。
堆:
- 作用:用于动态分配内存,存储在堆上的数据的生命周期由程序员管理。
- 使用:通过
new
(C++)或malloc
(C)等操作在堆上分配内存,程序员负责在适当的时候释放内存(delete
或free
)。堆上的数据可以在程序的不同部分共享。 收起
代码区
在C++程序中,代码区是存储程序执行代码的一部分内存区域。它通常被划分为两个主要部分:代码段和只读数据段
。
代码区通常指的是程序在内存中的一部分,而不是存储在硬盘上的代码。在计算机程序执行时,代码从硬盘上加载到内存中,其中的一部分被分配给代码区。代码区包括代码段和只读数据段,用于存储程序的可执行指令和只读的常量数据
代码段(Text Segment)
- 结构:代码段存储程序的可执行指令,即机器码。这是程序中实际执行的代码部分。
- 使用场景:包括程序的函数、方法、控制流等。这部分内存是只读的,程序在运行时不能修改代码段的内容。
示例:考虑以下简单的C++代码: