深入理解C语言之for循环

855 阅读6分钟

image.png

前言

今晚收到母校的几个大一学弟学妹们的请求,说C语言中的for循环结构比较难理解,学校老师讲的不怎么听懂,因为之前看过我写的 【深入理解Java集合框架 - 零】 | 数组 的文章,深入浅出通俗易懂,希望给他们讲解下。所以连夜赶制了这篇文章,从各个角度深入剖析讲解,希望刚进入编程世界的小伙伴的能有所收获。

编程是一门实操性比较强的技术,纸上得来终觉浅,绝知此事要躬行

千曲而后晓声,观千剑而后识器。虐它千百遍方能通晓其真意

一、概述

for循环是编程中最常用的循环结构,用于重复执行一段代码直到满足某个条件。

二、基本语法

for (初始化表达式; 条件表达式; 更新表达式) {
  // 循环体
}

三、组成及执行流程

image.png

3.1、初始化表达式:

  • 循环开始前执行一次
  • 通常用于初始化一个计数器变量
  • 可以是多条语句
  • 例如:int i = 0

3.2、条件表达式:

  • 每次循环开始时判断
  • 条件表达式值为真,则执行循环体
  • 条件表达式值为假,则退出循环
  • 例如:i < 10

3.3、更新表达式:

  • 每次循环体执行完毕后执行
  • 通常用于更新计数器变量
  • 可以是多条语句
  • 例如:i++

3.4、循环体:

  • 包含在{}大括号内的代码块,会在每次循环时执行

四、与其他循环结构的对比

4.1、while 循环

  • while循环在每次循环开始时判断条件表达式,如果条件为真,则执行循环体。
  • 适用于条件不确定的情况。
int main() {
    int i = 0;
    while (i < 5) {
        printf("i = %d\n", i);
        i++;
    }
    return 0;
}

4.2、do-while循环

  • do-while循环是至少执行一次循环体,然后在每次循环结束时判断条件表达式,如果条件为真,则继续执行循环体。
  • 适用于需要至少执行一次的情况。
int main() {
   int i = 0;
   do {
       printf("i = %d\n", i);
       i++;
   } while (i < 5);
   return 0;
}

五、示例

5.1、基本的for循环:

int main() {
    for (int i = 0; i < 5; i++) {
        printf("i = %d\n", i);
    }
    return 0;
}

5.2、计算数组的和:

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int size = sizeof(arr) / sizeof(arr[0]);
    int sum = 0;
    for (int i = 0; i < size; i++) {
        sum += arr[i];
    }
    printf("数组的和: %d\n", sum);  // 输出: 数组的和: 150
    return 0;
}

5.3、倒序遍历数组:

int main() {
    int arr[] = {10, 20, 30, 40, 50};
    int size = sizeof(arr) / sizeof(arr[0]);
    for (int i = size - 1; i >= 0; i--) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }
    return 0;
}

六、特殊用法

6.1、省略初始化表达式:

  • 如果在 for 循环外部已经初始化了计数器变量,可以省略初始化表达式。
int main() {
    int i = 0;
    for (; i < 5; i++) {
      printf("i = %d\n", i);
    }
    return 0;
}

6.2、省略条件表达式:

  • 如果省略条件表达式,for 循环将无限循环,除非在循环体内有 break 语句。
int main() {
    for (int i = 0;; i++) {  
      if (i >= 5) {  
         break;  
       }  
       printf("i = %d\n", i);  
    }
    return 0;
}

6.3、省略更新表达式:

  • 如果在循环体内已经更新了计数器变量,可以省略更新表达式。
int main() {
    for (int i = 0; i < 5;) {  
        printf("i = %d\n", i);  
        i++;  
    }
    return 0;
}

6.4、多个初始化及更新表达式:

  • 可在初始化表达式初始化多个变量
  • 可在更新表达式更新多个变量
int main() {
    for (int i = 0, j = 10; i < 5; i++, j--) {
        printf("i = %d, j = %d\n", i, j);
    }
    return 0;
}

6.5、死循环:

  • 忘记更新循环变量更新方式错误可能导致死循环
int main() {
    for (int i = 0; i < 5; ) {
        // 忘记更新 i
        printf("i = %d\n", i);
    }
    return 0;
}

6.6、嵌套循环:

需求1:打印二维数组:

int main() {
    // 定义一个二维数组
    int matrix[3][4] = {
        {1, 2, 3, 4},
        {5, 6, 7, 8},
        {9, 10, 11, 12}
    };
    // 外层循环遍历行
    for (int i = 0; i < 3; i++) {
        // 内层循环遍历列
        for (int j = 0; j < 4; j++) {
            // 打印当前元素
            printf("%d ", matrix[i][j]);
        }
        // 每打印完一行后换行
        printf("\n");
    }
    return 0;
}
输出结果:
1 2 3 4 
5 6 7 8 
9 10 11 12 

需求2:打印打印九九乘法表

int main() {
    // 外层循环控制行数
    for (int i = 1; i <= 9; i++) {
        // 内层循环控制列数
        for (int j = 1; j <= i; j++) {
            // 打印每一项乘法表达式
            printf("%d * %d = %2d  ", j, i, i * j);
        }
        // 每打印完一行乘法表后换行
        printf("\n");
    }
    return 0;
}
输出结果:
1 * 1 =  1  
1 * 2 =  2  2 * 2 =  4  
1 * 3 =  3  2 * 3 =  6  3 * 3 =  9  
1 * 4 =  4  2 * 4 =  8  3 * 4 = 12  4 * 4 = 16  
1 * 5 =  5  2 * 5 = 10  3 * 5 = 15  4 * 5 = 20  5 * 5 = 25  
1 * 6 =  6  2 * 6 = 12  3 * 6 = 18  4 * 6 = 24  5 * 6 = 30  6 * 6 = 36  
1 * 7 =  7  2 * 7 = 14  3 * 7 = 21  4 * 7 = 28  5 * 7 = 35  6 * 7 = 42  7 * 7 = 49  
1 * 8 =  8  2 * 8 = 16  3 * 8 = 24  4 * 8 = 32  5 * 8 = 40  6 * 8 = 48  7 * 8 = 56  8 * 8 = 64  
1 * 9 =  9  2 * 9 = 18  3 * 9 = 27  4 * 9 = 36  5 * 9 = 45  6 * 9 = 54  7 * 9 = 63  8 * 9 = 72  9 * 9 = 81  

需求3:打印1~100之间的素数

  • 素数的特点
    • 素数(质数)是指在大于1的自然数中,除了1它本身以外不再有其他因数的数。
    • 换言之,若一个数只能被1它自己整除,那么这个数就是素数(要点1)。
    • 奇偶性:除了最小的素数2是唯一的偶数素数外,其余所有素数都是奇数(要点2)。
bool isPrime(int n) {
    if (n <= 1) {
        return false;  // 1及以下的数不是素数
    }
    if (n == 2) {
        return true;   // 2是最小的素数
    }
    if (n % 2 == 0) {
        return false;  // 排除偶数
    }
    for (int i = 3; i * i <= n; i += 2) {
        if (n % i == 0) {
            return false;  // 如果能被i整除,则不是素数
        }
    }
    return true;  // 如果没有找到能整除的数,则是素数
}

int main() {
    printf("1到100之间的素数:\n");
    //遍历2到100的数
    for (int i = 2; i <= 100; i++) {
        if (isPrime(i)) {
            printf("%d ", i);
        }
    }
    printf("\n");
    return 0;
}

输出结果: 
1100之间的素数:
2 3 5 7 11 13 17 19 23 29 31 37 41 43 47 53 59 61 67 71 73 79 83 89 97 

思考:为什么使用 i*i <=n ?

  • 数学原理
    • 如果一个数 n 不是素数,那么它一定有一个不大于其平方根的因数。
    • 假设 n 有一个因数 d,且 d 小于等于 sqrt(n),那么 n / d 也是一个因数,且 n / d 大于等于 sqrt(n)
    • 因此,如果 n 有因数,那么至少有一个因数在 [2, sqrt(n)] 范围内。
  • 优化效果
    • 使用 i * i <= n 作为循环条件,可以将检查的范围从 [2, n-1] 缩小到 [2, sqrt(n)]
    • 大大减少了需要检查的次数,特别是对于较大的 n,这种优化效果非常明显。

七、总结

for 循环是编程语言中非常强大和灵活的循环结构,适用于各种需要重复执行的场景。通过合理设置初始化表达式条件表达式更新表达式,可以实现复杂的逻辑。

初学者若想掌握好for循环,需要逐步的感悟代码的执行流程彻底理解每一代码的作用,然后多练习和感悟,慢慢的就能熟练掌握了 !!!

码字不易,记得 关注 + 点赞 + 收藏 + 评论