前置知识:

前言 - 图1

一本通索引:https://gitee.com/wyloving/YiBenTongCode.git

深入探索for循环的进阶用法

前言 - 图2

在 C++ 中,for 循环是非常灵活的,它支持多种变种和用法,允许你省略其中的某些部分,甚至使用无限循环、反向循环等不同的结构。这种灵活性使得 for 循环在多种场景下都能有效使用。下面我将通过一些具体的示例来讲解 for 循环的不同变种用法。

基本的 for 循环语法

  1. for (初始化表达式; 判断表达式; 增量表达式) {
  2. // 循环体
  3. }

在每次迭代时,执行顺序如下:

  1. 初始化表达式:仅在第一次迭代之前执行一次,通常用来设置循环变量。
  2. 判断表达式:每次迭代之前执行,决定是否继续执行循环体。
  3. 增量表达式:每次迭代后执行,通常用来更新循环变量。

1. 省略循环条件表达式

你可以省略循环的判断条件表达式,创建一个无限循环。在这种情况下,循环会一直进行,直到程序内部通过 break 或其他条件来终止它。

  1. #include <iostream>
  2. int main() {
  3. int count = 0;
  4. // 无限循环
  5. for (; ; ) {
  6. std::cout << "Loop iteration: " << count << std::endl;
  7. count++;
  8. // 当 count 等于 5 时退出循环
  9. if (count >= 5) {
  10. break;
  11. }
  12. }
  13. return 0;
  14. }

2. 省略初始化表达式

for 循环中,你可以省略初始化部分,循环变量可以在循环外部定义或初始化。这样允许你在循环外部进行更复杂的初始化。

  1. #include <iostream>
  2. int main() {
  3. int count = 0;
  4. // 省略初始化部分,直接使用外部定义的 count
  5. for (; count < 5; count++) {
  6. std::cout << "Loop iteration: " << count << std::endl;
  7. }
  8. return 0;
  9. }

3. 省略增量表达式

for 循环的增量部分是可选的,如果你不需要增量表达式,可以将其省略。通过这种方式,你可以手动控制循环变量的变化。

  1. #include <iostream>
  2. int main() {
  3. int count = 0;
  4. // 省略增量部分
  5. for (; count < 5; ) {
  6. std::cout << "Loop iteration: " << count << std::endl;
  7. count += 2; // 自定义增量
  8. }
  9. return 0;
  10. }

在这个示例中,count 每次增加 2,而不是默认的 1。

4. 省略所有三个部分

for 循环的三个部分都可以省略,这样就可以让你手动控制循环的各个方面。例如,你可以将循环的控制完全交给 breakcontinue 语句。

  1. #include <iostream>
  2. int main() {
  3. int count = 0;
  4. // 省略所有三个部分
  5. for (;;) {
  6. std::cout << "Loop iteration: " << count << std::endl;
  7. count++;
  8. if (count == 3) {
  9. std::cout << "Breaking at count = " << count << std::endl;
  10. break; // 跳出循环
  11. }
  12. }
  13. return 0;
  14. }

5. 使用反向循环

for 循环也可以通过设置适当的初始值、判断条件和增量表达式来进行反向循环。比如,倒序打印一个数组的元素:

  1. #include <iostream>
  2. int main() {
  3. int arr[] = {1, 2, 3, 4, 5};
  4. int n = sizeof(arr) / sizeof(arr[0]);
  5. // 反向循环
  6. for (int i = n - 1; i >= 0; i--) {
  7. std::cout << arr[i] << " ";
  8. }
  9. return 0;
  10. }

6. 使用多个循环变量

for 循环的初始化、条件和增量部分都支持使用多个变量,并且你可以在这些部分中进行不同的操作。这对于复杂的循环结构非常有用。

  1. #include <iostream>
  2. int main() {
  3. // 使用多个循环变量
  4. for (int i = 0, j = 10; i < 5 && j > 5; i++, j -= 2) {
  5. std::cout << "i = " << i << ", j = " << j << std::endl;
  6. }
  7. return 0;
  8. }

7. 使用 for 循环遍历容器(C++11 及以上)

C++11 引入了基于范围的 for 循环,简化了数组或容器的遍历。这样你就可以不用显式地使用索引来遍历容器。

  1. #include <iostream>
  2. #include <vector>
  3. int main() {
  4. std::vector<int> vec = {1, 2, 3, 4, 5};
  5. // 基于范围的 for 循环
  6. for (int value : vec) {
  7. std::cout << value << " ";
  8. }
  9. return 0;
  10. }

8. 嵌套 for 循环

有时候,我们需要嵌套多个 for 循环来处理多维数据。比如,打印一个二维数组:

  1. #include <iostream>
  2. int main() {
  3. int arr[3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}};
  4. // 嵌套的 for 循环
  5. for (int i = 0; i < 3; i++) {
  6. for (int j = 0; j < 3; j++) {
  7. std::cout << arr[i][j] << " ";
  8. }
  9. std::cout << std::endl;
  10. }
  11. return 0;
  12. }

总结

C++ 中的 for 循环非常灵活,允许你根据需要省略其中的任何部分(初始化、条件判断、增量),甚至省略全部部分来控制循环的执行。你可以用它来处理常规的递增循环,也可以用它来实现倒序循环、嵌套循环等。通过灵活地应用这些变种,你可以让代码更加简洁和高效。

卫语句

“卫语句”(Guard Clauses)是编程中一种用于简化代码结构的常见设计模式,尤其是在处理多重条件判断时,它通过提前返回或退出函数,避免了过多的嵌套,从而使代码更加清晰和易于理解。

Guard Clauses 的概念

Guard Clause 的核心思想是,当某个条件不满足时,提前退出函数,避免后续的复杂嵌套。通过这种方式,减少了代码的层级,使得代码更加直观和简洁。

场景描述

假设我们要编写一个函数,根据学生的成绩来判断他们的评定等级。根据成绩的不同,给出以下评定:

  • 成绩大于等于 90 分:优秀
  • 成绩大于等于 75 分但小于 90 分:良
  • 成绩大于等于 60 分但小于 75 分:合格
  • 成绩小于 60 分:待及格

在不使用卫语句的情况下,代码可能会比较嵌套,像这样:

没有使用 Guard Clauses 的版本

  1. #include <iostream>
  2. void evaluateGrade(int score) {
  3. if (score >= 90) {
  4. std::cout << "优秀" << std::endl;
  5. } else {
  6. if (score >= 75) {
  7. std::cout << "良" << std::endl;
  8. } else {
  9. if (score >= 60) {
  10. std::cout << "合格" << std::endl;
  11. } else {
  12. std::cout << "待及格" << std::endl;
  13. }
  14. }
  15. }
  16. }
  17. int main() {
  18. int score = 82; // 你可以改变这个值来测试不同的成绩
  19. evaluateGrade(score);
  20. return 0;
  21. }

代码分析

在上面的代码中,每次我们用 else 来判断下一个区间的成绩。这种多重嵌套使得代码层级较深,尤其当条件判断较多时,代码可读性和维护性都会受到影响。

使用 Guard Clauses 优化后的版本

我们可以应用“卫语句”(Guard Clauses)模式,提前处理掉不符合条件的情况,然后在剩下的条件中进行处理。这种方式能有效减少代码的层级,使得代码更加简洁、直观。

  1. #include <iostream>
  2. void evaluateGrade(int score) {
  3. if (score >= 90) {
  4. std::cout << "优秀" << std::endl;
  5. return;
  6. }
  7. if (score >= 75) {
  8. std::cout << "良" << std::endl;
  9. return;
  10. }
  11. if (score >= 60) {
  12. std::cout << "合格" << std::endl;
  13. return;
  14. }
  15. std::cout << "待及格" << std::endl;
  16. }
  17. int main() {
  18. int score = 82; // 你可以改变这个值来测试不同的成绩
  19. evaluateGrade(score);
  20. return 0;
  21. }

代码分析

在这个版本中,我们通过使用 Guard Clauses 来简化代码:

  1. 每次判断一个条件时,如果条件满足,立即返回结果,跳出函数。
  2. 不再需要多个 else 语句来处理剩余条件。
  3. 逻辑非常清晰,从高到低依次进行判断,一旦条件满足,就结束函数,返回相应的评定。

优点总结

  1. 减少嵌套:通过提前返回,使得代码不再有层层嵌套的结构。每个判断条件都被处理完后立刻返回,使得代码的层级更浅,逻辑更清晰。
  2. 提高可读性:每个条件语句的意图更直观,避免了多重嵌套使代码变得难以理解。
  3. 简洁易维护:如果以后要更改成绩的评定条件,只需修改对应的 Guard Clause,而不会影响其他的代码逻辑。

通过这个简单的例子,你可以看到,卫语句(Guard Clauses)通过提前处理异常或边界情况,能够让代码更简洁、直观,也更易于维护。

c++ 内存结构

前言 - 图3

想一想:如果你是编译器,给你一段别人写的代码,你该怎么高度运行这段代码?

前言 - 图4

程序编程后生成可执行程序(exe)

二进制代码被调入“代码区域”(内存内)

这些内存区域在程序执行期间协同工作,每个区域有不同的生命周期、作用和管理方式。下面是对这些内存区块的关系和使用的详细说明:

代码区:

  • 作用:存储程序的机器码指令,包括执行代码和只读数据。
  • 使用:程序在启动时被加载到内存,指令和只读数据在代码区执行。

常量存储区:

  • 作用:存储不可修改的常量数据,如字符串常量、全局常量、const修饰的变量等。

  • 使用:常量数据在程序加载时被分配内存,通常以只读的方式存储。

全局/静态存储区:

  • 作用:存储全局变量和静态变量,其生命周期贯穿整个程序执行过程。
  • 使用:全局变量在程序启动时分配内存,静态变量在声明时分配内存。它们的数据在整个程序执行期间可读写。

栈:

  • 作用:用于存储函数调用信息、局部变量、临时数据等。遵循后进先出(LIFO)原则。
  • 使用:每个函数调用都会创建一个栈帧,包含局部变量和函数调用信息。栈帧在函数返回时被销毁。

堆:

  • 作用:用于动态分配内存,存储在堆上的数据的生命周期由程序员管理。
  • 使用:通过new(C++)或malloc(C)等操作在堆上分配内存,程序员负责在适当的时候释放内存(deletefree)。堆上的数据可以在程序的不同部分共享。 收起

代码区

在C++程序中,代码区是存储程序执行代码的一部分内存区域。它通常被划分为两个主要部分:代码段和只读数据段

代码区通常指的是程序在内存中的一部分,而不是存储在硬盘上的代码。在计算机程序执行时,代码从硬盘上加载到内存中,其中的一部分被分配给代码区。代码区包括代码段和只读数据段,用于存储程序的可执行指令和只读的常量数据

代码段(Text Segment)

  • 结构:代码段存储程序的可执行指令,即机器码。这是程序中实际执行的代码部分。
  • 使用场景:包括程序的函数、方法、控制流等。这部分内存是只读的,程序在运行时不能修改代码段的内容。

示例:考虑以下简单的C++代码: