C 语言学习笔记(7)

内容提要

  • 循环结构
    • 循环定义
    • 当型循环的实现
    • 直到型循环的实现
    • 循环的嵌套
    • 循环的应用场景
    • 基础算法模型
    • break与continue

循环结构

什么是循环结构

代码在满足某种条件的前提下,重复执行,就叫做循环结构

循环的分类

  • **无限循环:**其实就是死循环,程序设计中要谨慎使用。
  • **有限循环:**循环限定循环次数(for)或者终止循环条件(while,do…while)

循环的构成

  • **循环条件:**如循环次数,或者终止循环的出口
  • **循环体:**需要重复执行的代码。

当型循环的实现

特点:先判断,后执行,如果条件不满足,一次都不执行

**代表:**while、for

while

语法:

while (循环条件)
{
    循环语句;
}

//如果循环语句是单语句,可以省略花括号{}
while (循环条件) 循环单语句;
    while (循环条件) 
        循环单语句;
        
//注意:有些特殊场景下,循环体语句糅合到了循环条件中,所以省略掉了循环体
while (循环条件);

说明:

  1. 循环条件的返回值必须是逻辑值(使用非0表示真,使用0表示假,C语言底层使用1表示真)C99版本引入了一个stdbool.h,可以使用true或者false来表示真假,这里的true和false实际上就是定义的两个符号常量,true的值是1,flase的值0。
  2. {}包起来的内容整体称之为循环体
  3. 我们要在循环体中控制循环条件的变化,也可以在循环条件中控制循环条件的变化,否则产生死循环。

执行过程:

在这里插入图片描述

**特点:**先判断,后执行,循环体语句有可能一次都不执行。

案例

  • 需求:求1~100的累加和

  • 分析:

    • 创建一个变量sum = 0,用来接收累加和
    • 创建一个变量i,用来表示计算数,我们会给i一个初始值i = 1,每次循环的时候i++
    • 在循环中,使用sum += i,完成累加和运算
    • 同时我们要限定循环的条件,也就是i的范围:i <= 100
      • 第1次:sum += i = 0 + 1 = 1
      • 第2次:sum += i = 1 + 2 = 3
      • 第3次:sum += i = 3 + 3= 6
  • 代码:

    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        // 创建一个变量sum,用来接收累加和
        int sum = 0; // 这里一定要初始化, 因为sum要参与计算
        
        // 创建一个循环变量i,i循环初始值,因为i要参与计算
        int i = 1; // 循环变量
        
        // 循环求累加和
        while (i <= 100)// 循环条件
        {
            // 实现加法运算
            sum += i; // 等价于 sum = sum + i;
            // 改变循环条件,让其逼近循环的出口
            i++;
        }
        // 输出sum
        printf("1~100的累加和是%d\n", sum);
        
        return 0;
     }
    
  • 运行结果

    在这里插入图片描述

案例:

  • 需求:求1~100以内的偶数和

  • 分析:

    • 创建两个变量,sum = 0用来存储累加的偶数和, i = 2用做循环变量,存储自然数
    • 创建一个循环,设置循环条件i <= 100
    • 在循环体内,需要一个if来校验i是否是偶数(if(i % 2 == 0)),若果满足,就在if中求偶数和:sum += i
    • 在循环体的最后一行,需要改变循环变量i的值,用来逼近循环出口
    • 循环结束后,才能输出sum
  • 代码

    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        // 创建两个变量,sum存储累加和,i存储自然数(i是循环条件)
        int sum = 0, i = 2;
        
        // 创建一个循环,实现偶数和运算
        while (i <= 100)
        {
            // 计算偶数和
            if (i % 2 == 0) sum += i; i++;  // 注意:此时i++是在if语句外部
        }
        
        // 输出偶数和
        printf("1~100以内的偶数和是:%d\n", sum);
        
        return 0;
    }
    
  • 运行结果:

    在这里插入图片描述

while的特殊写法:

while(1)   // 死循环   while(i=5)
for

原则上:for循环能实现的,while循环也能实现,for循环可以看做是while循环的一种特殊写法,for循环胜在结构清晰。

语法:

for(① 表达式1; ② 表达式2; ③表示式3)
{
    ④ 循环体语句;
}

//如果循环体语句是单语句,也是可以沈略{}的
for(① 表达式1; ② 表达式2; ③表示式3) ④ 循环体语句;

for(① 表达式1; ② 表达式2; ③表示式3) 
    ④ 循环体语句;

说明:

()中可以仅保留两个;;,举例:for(;;),此时就是for循环中的一种死循环的体现。

表达式1循环变量,我们需要赋初值,循环变量也可以单个,也可以是列表,多个循环变量之间使用逗号分隔:

//循环变量是单个
for (int i = 0;...;...){..}

//循环变量是列表
for (int i = 0, j = len -1; ... ; ...){...}

表达式2循环条件,用来限制循环的次数,循环条件支持关系表达式,逻辑表达式等,举例:

for (...; i < 10 && j >= 0;...){...}

表达式3修改循环变量,支持列表,列表使用逗号分隔,这里可以使用赋值表达式,举例:

for (...;...;i++,j--){...}

⑤执行顺序:①②④③ → ②④③ → ②④③ → … → ②

执行过程:

在这里插入图片描述

特点:先判断,后执行,循环体语句有可能一次都不执行。

案例

  • 需求:计算1~100以内偶数和

  • 代码:

    #include <stdio.h>
    
    /**
    * 使用while实现
    */
    
    int while_test()
    {
        // 创建两个变量,sum存储累加和,i存储自然数(i是循环条件)
        int sum = 0, i = 2;
        
        // 创建一个循环,实现偶数和运算
        while (i <= 100)
        {
            // 计算偶数和
            if (i % 2 == 0) sum += i; i++;  // 注意:此时i++是在if语句外部
        }
        // 输出偶数和
        printf("1~100以内的偶数和是:%d\n", sum);
        
        return 0;
    }
     
    /**
    * 使用for实现
    */
     int for_test1()
     {
        // 创建两个变量,sum存储累加和,i存储自然数(i是循环条件)
        int sum = 0, i = 2;
        
         // 创建一个循环,实现偶数和运算
        for (;i <= 100;)
        {
            // 计算偶数和
            if (i % 2 == 0) sum += i; i++;  // 注意:此时i++是在if语句外部
        }
        // 输出偶数和
        printf("1~100以内的偶数和是:%d\n", sum);
        
         return 0;
     }
     
    /**
    * 使用for实现
    */
    int for_test2()
    {
        // 创建两个变量,sum存储累加和,i存储自然数(i是循环条件)
        int sum = 0, i;
        // 创建一个循环,实现偶数和运算
        for (i = 2; i <= 100; i++)
        {
            // 计算偶数和
            if (i % 2 == 0) sum += i; 
        }
        // 输出偶数和
        printf("1~100以内的偶数和是:%d\n", sum);
        return 0;
    }
    
    int main(int argc,char *argv[])
    {
        while_test();  
        for_test1();
        for_test2();
        
        return 0;
     }
    

案例:

  • 需求:用户通过键盘输入一个整数n,求n的阶乘。例如: n = 4,n的阶乘1 * 2 * 3 * 4

  • 代码:

    #include <stdio.h>
    #include <math.h>
    
    int main(int argc,char *argv[])
    {
        // 创建一个变量,用来存储计算数
        unsigned long n;
        // 创建一个变量,用来存储乘积
        unsigned long r = 1;
        
        // 通过控制台,给n赋值
        printf("请输入一个整数:\n")
        scanf("%lu", &n);
        
        // 通过for循环计算
        for (int i = 1; i <= fabs(n); i++) r *= i;
        
        printf("1~%lu之间阶乘的结果是%lu\n", (unsigned long)fabs(n), r);
        
        return 0;
     }
    
  • 注意:阶乘计算,如果乘积超过变量存储的范围,会溢出,结果计算可能不准确。

for实现死循环:

for(表达式1;;表达式3) {..}
for(表达式1;;) {..}
for(;;表达式3) {..}
for(;;) {}
for(表达式1;表达式2;) {}
...
循环实现的三要素
  • 循环变量的初始化,举例:int i = 2;
  • 循环条件,举例:i < 100
  • 循环变量的更新,举例:i++

举例:

  • 需求:求斐波拉切数列前20个

  • 分析:

    • 斐波拉切数列指的是符合一定规则的数列,举例:1,1,2,3,5,8…

    • 斐波拉切数列的特点:第3个数等于前两个数之和,最开始的第1,2个数是固定的

    • 代码:

      #include <stdio.h>
      
      int main(int argc,char *argv[])
      {
          // 创建一个变量,存储第1,2个数,默认都是1
          int f1 = 1, f2 = 1;
          
          // 循环变量
          int i = 1;  // 每一次循环产生2个数,也就循环10次
          
          // 通过循环生成数列
          for (; i <= 10; i++)
          {
              // 输出当前的两个数列
              printf("%-12d%-12d\n",f1,f2);
              // 计算后续的第1,2个数
              f1 += f2; // f1 = f1 + f2 = 1 + 1 ...
              f2 += f1; // f2 = f2 + f1 = 2 + 1 ...
          }
          
          return 0;
       }
      
    • 运行结果:

      在这里插入图片描述

课堂练习

**需求:**通过键盘录入一个整数,判断这个整数是否是水仙花数。

示例:

#include <stdio.h>
#include <math.h>

int main() {
    int num, originalNum, n = 0, sum = 0;
    
    printf("请输入一个整数: ");
    scanf("%d", &num);
    
    originalNum = num;
    
    // 计算数字的位数
    while (num != 0) {
        num /= 10;
        n++;
    }
    
    num = originalNum; // 恢复原始数值
    
    // 计算各位数字的n次方之和
    while (num != 0) {
        int digit = num % 10;
        sum += pow(digit, n);
        num /= 10;
    }
    
    // 判断并输出结果
    if (sum == originalNum) {
        printf("%d 是水仙花数。\n", originalNum);
    } else {
        printf("%d 不是水仙花数。\n", originalNum);
    }
    
    return 0;
 }

直到型循环的实现

特点:先执行,后判断,不管条件是否满足,至少执行一次。

**代表:**do…while,goto(已淘汰,不推荐使用)

do…while

语法:

循环变量;
do
{
    循环体;
}while(循环条件);

说明:

循环条件的返回值必须是逻辑值(0和非0(计算机返回1))

{}包起来的内容称之为循环体

③我们要在循环体中控制循环条件的变化,否则会产生死循环。

执行过程:

在这里插入图片描述

特点:先执行,后判断,不管条件判断是否满足,循环体语句至少执行一次。

案例:

  • 需求:求1~100以内偶数和

  • 分析:

    • 创建一个变量,用来存储sum, sum = 0
    • 创建一个循环变量, i = 2
    • 创建一个do…while循环,再循环体中,校验1 % 2 ==0,若果满足,就是实现sum +=i
    • 计算完成,在循环体的末行,对循环变量进行更新i++
    • 限制循环的出口:i <= 100
    • 循环结束,打印输出sum的值
  • 代码

    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        // 创建一个变量,存储偶数和
        int sum = 0;
        // 创建一个循环变量
        int i = 2;
        
        // 创建一个循环,实现偶数和的计算
        do
        {
            // 校验偶数
            if (i % 2 == 0) sum += i;
            // 更新循环变量,逼近出口
            i++;
        } while(i <= 100);
        
        // 打印输出sum
        printf("1~100以内的偶数和是%d\n", sum);
        
        return 0;
     }
    

案例:

  • 需求:用C语言编写简单猜数字游戏代码。游戏规则是程序随机生成一个1到100之间的数字,玩家通过输入猜测数字,程序会提示猜测是太大还是太小,直到玩家猜中为止。

  • 代码

     #include <stdio.h>
     #include <stdlib.h>
     #include <time.h>
     int main(int argc,char *argv[])
     {
        // 创建变量number(随机数)、guess(用户猜测的数字)、count(计数)
        int number, guess, count = 0;
        // 设置随即种子
        srand(time(NULL));
        // 生成随机数 1 ~ 10之间的随机数
        number = rand() % 10 + 1; // 此时生成 0 ~ 9之间的随机数,包含头不含尾
        printf("-----------------------------\n");
        printf("|    猜数字小游戏v1.0       |\n");
        printf("-----------------------------\n");
        printf("我已经想了一个1~10之间的数,你猜猜是多少?\n");
        // 猜数字,猜不对一直猜,猜到对为止
        do
        {
            printf("请输入你的猜测:\n");
            // 接收scanf的返回值
            int result = scanf("%d",&guess);
            // 对非法输入进行校验(比如,我们要求输入数值,但是不小写输入了非数值,此时就是非法输入)
            if (result != 1)
            {
                // 如果输入的不是数值,就是非法的
                // 清空输入缓冲区,重新从外部设备读取数据到缓冲区
                while(getchar() != '\n'); // getchar 类似 scanf("%c",c);
                printf("请输入数字!\n");
                continue; // 这次循环,跳过不算
            }
            // 计数
            count ++;
            // 校验
            if (guess > number) printf("太大了,再猜一次。\n");
            else if (guess < number) printf("太小了,再猜一次。\n");
             else
     {
                  printf("恭喜你,猜对了!\n");
     printf("你一共猜了%d次。\n", count);
     }
        } while (guess != number); // 等价于 guess > number || guess < number
    
     return 0;
     }
    
  • 运行结果

    在这里插入图片描述

goto(了解)

语法:

goto 标签

标签:标明目标代码的位置,是一个不加""的字符串。

案例:
  • 需求:求1~100以内的偶数和

  • 代码:

    #include <stdio.h>
     
    int main(int argc,char *argv[])
    {
         // 创建一个变量,用来存放偶数和
         int sum = 0;
     
         // 创建一个循环变量
         int i = 2;
     
     // 定义goto标签,名字自己取,符合标识符命名即可
     loop:
         // 过滤偶数
         if (i % 2 == 0) sum += i; // 偶数和计算
         i++; // 更新循环变量
     
         // 循环条件
         if (i <= 100)  goto loop; // 标签loop由goto触发
     
         printf("1~100以内的偶数和是%d\n",sum);
     
         return 0;
     }
    
  • 注意事项

    1. 可读性:goto语句会破坏代码的结构和可读性,使得代码难以理解和维护。因此,应尽量避免使用。
    2. **替代方案:**大多数情况下,可以使用循环、条件语句、函数等结构来替代goto语句,使代码更加清晰和易于管理。
    3. **嵌套限制:**虽然goto语句可以跨函数跳转(即跳转到另一个函数中的标签),但这种用法是不合法的,并且会导致编译错误。goto语句只能在同一函数内部跳转。
    4. 错误处理:在某些情况下,goto语句可以用于错误处理,例如从嵌套的多层循环中跳出。但即使在这种情况下,也应谨慎使用,并考虑是否有更好的替代方案。

总结
虽然goto语句在C语言中是合法的,并且有时可能看起来很方便,但过度使用或不当使用会导致代码质量下降。因此,建议尽量避免使用goto语句,而是采用更结构化和可维护的编程方法。

循环的嵌套

3种循环(while、for、do…while)可以相互嵌套的。在前一个循环结构中又嵌套一个循环结构。例如:

在这里插入图片描述
)

案例:

  • 需求:九九乘法表

  • 分析:

    • 我们发现九九乘法表整体其实就是一个9行的直角三角形

    • 同时发现:每一行显示的列数最多不超过行,第1行1列,第2行2列…第9行9列

      假定:行用i表示,列用j表示,i和j的关系:j <=i

    • 在实现的时候,我们发现需要同时控制行和列的变化,在编程中,行列式需要通过for双层嵌套实现(****)

  • 代码:

    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        printf("--- 九九乘法表 ---\n");
        
        // 外层循环:生成行,有9行
        for (int i = 1; i <= 9; i++)
        {
            // 内层循环:生成列,列受到行的影响, 列 <= 行
            for (int j = 1; j <= i; j++)
            {
                // 生成算式
                printf("%d×%d=%d\t",j,i,j*i);
            }
            // 每一行所有列输出完毕,一定要换行
            printf("\n");
        }
        
        printf("\n");
        
        return 0;
     }
    
  • 运行结果:

    在这里插入图片描述

案例
  • 需求:求100~200之间所有的素数(素数又被称作质数)

  • 分析

    • 什么是素数:只能被1和自身整除的数叫做素数或者质数。
  • 代码:

    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        // 创建一个变量,存放100~200之间的自然数
        int num = 100;
        // 定义一个循环变量
        int i;
        // 定义一个标记,用来标记这个num是否是素数
        int flag;
        
        // 外层循环:生成100~200之间的自然数
        for (; num <= 200; num++)
        {
            // 每一个num生成的时候,我们默认其为素数
            flag = 1;
            // 内层循环,我们让每一个生成的num 除 2 ~ num / 2之间的数,一旦出现能整除的情况,就说明这个num不是素数
            for (i = 2; i < num / 2; i++)
            {
                // 测试整除的情况
                if(num % i == 0)
                {
                    flag = 0;
                    break;// 循环结束,只要出现一次能整除的情况,就说明这个数不是素数
                }
            }
            // 校验完毕,验证这个num是否是素数
            if(flag) // 等价于 flag == 1
                printf("%-4d",num);
        }
        printf("\n");
        
        return 0;
     }
    
  • 运行结果:

    在这里插入图片描述

循环结构的典型应用场景

  • 求累和:举例:``1+2+3…+100的和,sum = 0`
  • 求累积:举例:1*2*3..*100的积,result = 1
  • 求均值:举例:(1+2+3...+100)/100的值
  • 求极值:举例:12,34,56,67中的最大值、最小值
  • 元素遍历:常用于数组元素的遍历。

基础算法模型

  1. 累和
    • 定义一个变量(sum),并赋初值为0;
    • 该变量累加(+=)每一个数据项(i);
    • 当访问完每一个数据项,此时该变量的取值就是累加和的结果。
  2. 累乘
  • 定义一个变量,并赋初值为1;
  • 用该变量累乘(*=)每一个数据项;
  • 当访问完每一个数据项,此时该变量的取值就是累乘的结果。
  1. 极值(多应用于数组)
    • 定义一个变量,并赋初值为第一个数据项;
    • 从第二个数据项开始,依次于该变量进行比较,如果大于/小于该变量,则将当前数据项的 数据赋值给该变量。
    • 当访问完每一个数据项,此时该变量的取值就是求极值的结果。

break和continue

break

功能:

①用在switch中,用来跳出switch的case语句;如果case没有break,可能会产生case穿透。

②用在循环中(while、do…while、for),提前结束循环,也就是跳出整个循环。

说明:

①break不用用于循环语句和swich语句之外的任何其他语句中。

②break只能终止并跳出最近一层的循环结构,简而言之,就是只能跳出一层循环。

举例:

**帅同学过年的时候,家里安排了5场相亲,帅哥相到第3个时候相中了,此时剩余2场相亲取消。

图示:

在这里插入图片描述

案例:

  • 需求:参与分支结构的考试,如果考试及格

  • 代码:

    #include <stdio.h>
    
    int main(int argc,char *argv[])
    {
        printf("分支结构考试!\n");
        
        // 创建一个变量,用来存储成绩
        int score;
        
        // 创建一个循环,实现重复考试
        do
        {
            printf("磊哥开始考试...\n");
            scanf("%d", &score);
            
            // 考试及格,就终止循环
            if (score >= 60) break;
            
            printf("很遗憾,没有及格,继续考!\n"); // break执行后,这个代码不执行 
        
        }while(1); // 死循环
        
        printf("考试结束!\n");
        
        return 0;
     }
    
  • 运行结果

    在这里插入图片描述

continue

**功能:**continue语句不会结束整个循环,而是跳过本次循环尚未执行的语句,进入下一次循环。

举例说明:

**帅同学过年的时候,家里安排了5场相亲,帅哥相到第3个时候相中了,姑娘已经结婚了,帅哥跳过,继续剩余2场相亲。

说明:

①仅用于循环语句中
②在嵌套循环的情况下,continue语句只对包含它的最近一层循环其作用,只能有用单层循环。

图示:

在这里插入图片描述

案例
  • 需求:求1~100以内的偶数和

  • 代码:

    #include<stdio.h>
    
    /**
    * 不使用continue
    */
    int for_test1()
    {
        // 创建两个变量,sum存储累加和,i存储自然数(i是循环条件)
        int sum = 0, i;
        
        // 创建一个循环,实现偶数和运算
        for (i = 2; i <= 100; i++)
        {
            // 计算偶数和
            if (i % 2 == 0) sum += i; 
        }
     
        // 输出偶数和
        printf("1~100以内的偶数和是:%d\n", sum);
     
        return 0;
    }
    /**
    * 使用continue
    */
    int for_test2()
    {
        // 创建两个变量,sum存储累加和,i存储自然数(i是循环条件)
        int sum = 0, i;
        // 创建一个循环,实现偶数和运算
        for (i = 2; i <= 100; i++)
        {
            // 跳过奇数
            if(i % 2 != 0) continue;
            // 计算偶数和
            sum += i; // 一旦执行上面的continue,这句代码就不会执行
        }
        
        // 输出偶数和
        printf("1~100以内的偶数和是:%d\n", sum);
        return 0;
        
    int main(int argc,char *argv[])
    {
        for_test1();
        
        return 0;
     }
    
跳出多层循环(扩展)

跳出多层循环是不能直接使用break和continue实现的,因为它们只能跳出单层循环,跳出多层循 环,需要我们自定一定标志位进行跳出(标志位也可以配合break使用)。

案例:
#include <stdio.h>
   
int fun0()
{
       // 定义一个标志位
       int is_flag = 1;// 默认循环成立
       // 定义一个变量,用来接收控制台输入
       char fu;
   
       // 循环
       while(is_flag)
       {
           printf("外层循环执行的内容..\n");
   
           while(is_flag)
           {
               printf("内存循环执行的内容..\n");
               scanf("%c",&fu);
               if(fu == 'Y' || fu == 'y')
               {
                   is_flag = 0;
               }
           }
       }
   }
   
   int fun1()
   {
       // 定义一个标志位
       int is_flag = 1;// 默认循环成立
       // 定义一个变量,用来接收控制台输入
       char fu;
   
       // 循环
       while(is_flag)
       {
           printf("外层循环执行的内容..\n");
   
           while(1)
           {
               printf("内存循环执行的内容..\n")
               scanf("%c",&fu);

               if(fu == 'Y' || fu == 'y')
               {
                   is_flag = 0;
                   break;
               }
           }
       }
   }

int main()
{
    fun0();
    
    fun1();
}

注意:如果是多层循环(嵌套循环),进的时候是从外到内,跳出的时候是从内到外。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值