C语言第 6 天学习笔记:循环结构与基础算法

C语言第06天学习笔记:循环结构详解

内容提要

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

分支结构回顾

单分支

  • if...

双分支

  • if...else...
  • 三目运算符: 表达式1(条件表达式) ? 表达式2(语句1):表达式(语句2);(适用于输出型单语句的双分支)

示例:判断一个数是偶数还是奇数

#include <stdio.h>
int main()
{
    int num = 5;
    
    // 双分支:if..else
    if (num % 2 == 0) printf("%d是偶数!\n", num);
    else printf("%d是奇数!\n", num);
    
    // 双分支:三目运算符
    // 方式1
    (num % 2 == 0) ? printf("%d是偶数!\n", num) : printf("%d是奇数!\n", num);
    // 方式2
    printf("%s\n", (num % 2 == 0) ? "偶数" : "奇数");
    // 方式3
    char *p = (num % 2 == 0) ? "偶数" : "奇数";
    printf("%d是%s!\n", num, p);
    
    return 0;
}

多分支

  • if...else...嵌套(不推荐)
  • if...else if....else
  • switch..case..

循环结构

什么是循环结构

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

循环的分类

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

循环的构成

  • 循环条件:如循环次数或者循环的出口。
  • 循环体:需要重复执行的代码(也就是{}包裹的内容)

当型循环的实现

特点:先判断,后执行,如果条件不满足,循环体一次都不执行。
代表:whilefor

while循环

语法

【1】循环变量;
while (【2】循环条件)
{
    【3】循环语句;
    【4】更新循环变量;
}

注意

  • 如果循环语句是单语句,可以省略{}
    while (循环条件) 循环单语句;
    while (循环条件)
        循环单语句;
    
  • 有些特殊场景下,循环体语句糅合到了循环条件中,所以省略掉循环体
    while (循环条件); 
    // 例如:int i = 1; while(i++ <= 5); printf("%d\n",i);// 输出7
    

说明

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

执行过程

在这里插入图片描述

特点: 先判断循环条件,若为真则执行循环体语句,之后更新循环变量,再继续判断循环条件,依此往复,直到循环条件为假时结束循环。

案例1:求1~100的累加和

分析:

  • 创建一个变量sum = 0,用来接收累加和
  • 创建一个循环变量i,用来表示计算数,给一个默认值1,每次循环的时候 i++
  • 在循环中,使用 sum += i(sum = 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[])
{
    // 需求:求1~100的累加和
    // 创建一个变量sum = 0,用来存储累加和,因为参与计算,所以必须有初始值
    int sum = 0;
    
    // 创建一个循环变量并初始化
    int i = 1;
    
    // 通过一个循环生成1~100的自然数,并实现累加和运算
    while (i <= 100) // 循环条件,限制循环变量的变化,循环的出口限制
    {
        // 累加和运算
        sum += i;
        // 更新循环变量,逼近出口
        i++; // 等价于 i = i + 1、i += 1、++i
    }
    
    printf("1~100的累加和是%d\n", sum);
    
    return 0;
}

运行结果:1~100的累加和是5050

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

分析:

  • 创建一个变量sum = 0,用来存储累加的偶数和
  • 创建一个循环变量i = 2,存储参与运算的自然数(最小的偶数是2)
  • 创建一个循环,设置循环条件 i <= 100
  • 在循环体内,需要用一个if校验i是否是偶数,如果满足条件,就实现偶数和的计算:sum += i
  • 在循环体的最后一行,需要更新循环变量,作用是逐渐逼近循环的出口
  • 循环结束后,才能输出sum。

代码:

#include <stdio.h>
int main(int argc, char *argv[])
{
    // 创建一个变量sum = 0,用来存储累加的偶数和
    int sum = 0;
    // 创建一个循环变量i = 2,存储参与运算的自然数(最小的偶数是2)
    int i = 2;
    // 创建一个循环,设置循环条件 i <= 100
    while (i <= 100)
    {
        // 在循环体内,需要用一个if校验i是否是偶数,如果满足条件,就实现偶数和的计算:sum += i
        if (i % 2 == 0)
            sum += i;
        // 在循环体的最后一行,需要更新循环变量,作用是逐渐逼近循环的出口
        i++;
        // if (i % 2 == 0) sum += i; i++; // 等价于上面代码
    }
    // 循环结束后,才能输出sum
    printf("1~100以内的偶数和是%d\n", sum);
    
    return 0;
}

运行结果:1~100以内的偶数和是2550

案例3:判断一个整数是否是水仙花数

分析:

  • 拆分一个n位的数,比如拆分为如:个位、十位、百位…

  • 要求这个n位数的每一位的n次幂相加等于这个数本身:

代码:

#include <stdio.h>
#include <math.h>
int main(int argc, char *argv[])
{
    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;
        // 计算这个数的n次幂,并相加
        sum += pow(digit, n); // pow(运算数, 运算数的n次方)
        // 将需要拆除的位保留在个位
        num /= 10;
    }
    
    // 校验并输出
    if (sum == originalNum)
        printf("%d 是水仙花数。\n", originalNum);
    else
        printf("%d 不是水仙花数。\n", originalNum);
    
    return 0;
}

编译命令gcc demo06.c -lmmath.h中有些函数属于GNU扩展函数,不是C语言标准,需要指定链接数学库)-lm 意思是链接数学库,

for循环

原则上,for循环能实现的,while循环也可以实现,for循环可以看做是while循环的一种特殊写法,for循环胜在结构清晰,非常适合于知道循环次数的循环。

语法

for (【1】循环变量; 【2】循环条件; 【4】更新循环变量)
{
    【3】循环语句;
}

注意

  • 如果循环体语句是单语句,也可以省略{}
    for (【1】循环变量; 【2】循环条件; 【4】更新循环变量)【3】循环语句;
    for (【1】循环变量; 【2】循环条件; 【4】更新循环变量)
        【3】循环语句;
    
  • ()中可以仅保留两个;,例如for (;;),此时这种写法就是死循环的一种体现。
  • 循环变量:需要赋初始值,循环变量可以是单个,也可以是列表,多个循环变量之间使用逗号分隔。
    // 循环变量是单个
    for (int i = 0;..;..) {..}
    // 循环变量是多个
    for (int i = 0,j = len -1;..;..){..}
    
  • 循环条件:用来限制循环的次数,循环条件支持任意表达式(常用的有关系表达式,逻辑表达式)。
    for (..;i < 10 && j >= 0;..){..}
    
  • 更新循环变量:支持列表,列表使用逗号分隔,这里可以使用赋值表达式。
    for (..;..;i++,j--){..}
    
  • 执行顺序:①②③④ → ②③④ → ②③④ → … → ②

执行过程

在这里插入图片描述

先求解表达式1,再判断表达式2,如果为真则执行循环体,之后求解表达式3,再继续判断表达式2,依此往复,直到表达式2为假时结束循环。特点是先判断,后执行,循环体语句有可能一次都不执行。

案例:计算1~100以内的偶数和

代码:

#include <stdio.h>

/**
 * 使用while实现
 */
int while_test()
{
    int sum = 0;
    // 循环变量(需要初始化)
    int i = 2;
    // 循环条件
    while (i <= 100)
    {
        // 循环语句
        if (i % 2 == 0) sum += i;
        // 更新循环变量
        i++;
    }
    // 打印输出
    printf("1~100以内的偶数和是%d\n", sum);
    
    return 0;
}

/**
 * 使用for实现(不推荐)
 */
int for_test()
{
    int sum = 0;
    // 循环变量(需要初始化)
    int i = 2;
    // 循环条件
    for (;i <= 100;)
    {
        // 循环语句
        if (i % 2 == 0) sum += i;
        // 更新循环变量
        i++;
    }
    // 打印输出
    printf("1~100以内的偶数和是%d\n", sum);
    
    return 0;
}

/**
 * 使用for实现(推荐)
 */
int for_test2()
{
    int sum = 0;
    
    // 循环条件
    for (int 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_test();
    for_test2();
    
    return 0;
}

案例:求n的阶乘(n由控制台输入)

代码:

#include <stdio.h>
// 数学库对应的头文件
#include <math.h>

int main(int argc, char *argv[])
{
    // 创建一个变量,用来存储计算数
    unsigned long n;
    // 创建一个变量,用来存储乘积 size_t 是C语言中 unsigned long的别名
    size_t r = 1;
    
    // 通过控制台,给n赋值
    printf("请输入一个整数:\n");
    scanf("%lu", &n);
    
    // 通过for循环计算
    for (int i = 1; i <= fabs(n); i++) r *= i;
    
    printf("1~%lu的乘积结果是%lu\n", (size_t)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个数是固定的,都是1

代码:

#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,1 
        // 计算得到新的f1,f2
        f1 += f2; // f1 = f1 + f2; // 第1次:f1 = 1+1 = 2 ..
        f2 += f1; // f2 = f2 + f1; // 第1次:f2 = 1 + 2 = 3 ..
    }
    return 0;
}

运行结果:
在这里插入图片描述

直到型循环的实现

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

do…while循环

语法

【1】循环变量;
do
{
    【2】循环体语句;
    【3】更新循环变量;
} while(【4】循环条件);

说明

  • 循环条件的返回值必须是逻辑值(0和非0(计算机返回1))
  • {}包起来的内容称之为循环体
  • 要在循环体中控制循环条件的变化,否则会产生死循环。

执行过程

在这里插入图片描述

先执行循环体语句,再判断表达式,如果表达式为非0则继续执行循环体语句和更新循环变量,依此往复,直到表达式为0时结束循环。特点是先执行,后判断,不管条件是否满足,循环体语句至少执行一次。

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

分析:

  • 创建一个变量,用来存储偶数和sum = 0
  • 创建一个循环变量,i = 2
  • 创建一个do…while循环,在循环体中,校验 i % 2 == 0,如果满足,就实现偶数和计算 sum += i
  • 计算完成,在do…while循环体的末行,更新循环变量,使其逼近出口
  • 限制性循环的出口:i <= 100 (i > 100 ,循环结束)
  • 循环结束,打印输出sum的值

代码:

#include <stdio.h>

int main(int argc, char *argv[])
{
    // 创建一个变量,用来存储偶数和sum = 0 
    int sum = 0;
    // 创建一个循环变量,i = 2
    int i = 2;
    // 创建一个do..while循环,在循环体中,校验i % 2 == 0,如果满足,就实现偶数和计算 sum += i
    do
    {
        if (i % 2 == 0) sum += i;
        // 计算完成,在do..while循环体的末行,更新循环变量,使其逼近出口
        i++;
    } while (i <= 100); // 限制性循环的出口:i <= 100 (i > 100,循环结束)
    
    // 循环结束,打印输出sum的值
    printf("1~100以内的偶数和是%d\n", sum);
    return 0;
}

案例:简单猜数字游戏

#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之间的随机数 rand() % 最大范围整数,生成的实际数的范围 0 ~ 最大范围整数 - 1
    number = rand() % 10 + 1;

    // 设计头
    printf("----------------------\n");
    printf("|  猜数字小游戏v1.0  |\n");
    printf("----------------------\n\n");

    printf("我已经想了一个1~10之间的整数,你猜猜是多少?\n");

    // 猜数字,猜不对,就一直猜,猜到对为止
    do
    {
        printf("请输入你的猜测:\n");
        // 接收scanf的返回值
        int result = scanf("%d", &guess);
        // 对非法输入进行校验(比如:我们要求输入数值,但是不小心输入了非数值,此时就是非法输入)
        if (result != 1)
        {
            // 如果输入的不是数字,就是非法的,也就是输入的内容类型跟scanf提供的格式化符号不匹配
            // 每次重新输入前,需要清空输入缓冲区,重新从外部设备获取输入到缓冲区
            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语句是C语言中一种跳转语句,它可以使程序的执行流程直接跳转到指定的标签位置。

语法

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语句只能在同一函数内部跳转,不能跨函数跳转,否则会导致编译错误。
  4. 错误处理:在某些情况下,goto语句可以用于错误处理,例如从嵌套的多层循环中跳出,但即使在这种情况下,也应谨慎使用,并考虑是否有更好的替代方案。

总结

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

循环结构的典型应用场景

  • 求累和:如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;
    • 用该变量累乘(*=)每一个数据项;
    • 当访问完每一个数据项,此时该变量的取值就是累乘的结果。
  3. 极值(多应用于数组)

    • 定义一个变量,并赋初值为第一个数据项;
    • 从第二个数据项开始,依次与该变量进行比较,如果大于/小于该变量,则将当前数据项的数据赋值给该变量;
    • 当访问完每一个数据项,此时该变量的取值就是求极值的结果。

循环的嵌套

三种循环(while、for、do…while)可以相互嵌套,即在一个循环结构中又包含另一个循环结构。
在这里插入图片描述

案例:九九乘法表

分析:

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

  • 同时发现:每一行显示的列的数量不超过所在行,第1行1列,第2行2列…

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

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

代码:

#include <stdio.h>
int main(int argc, char *argv[])
{
    printf("--- 九九乘法表 ---\n");
    
    // 外层循环:生成行,1~9
    for (int i = 1; i <= 9; i++)
    {
        // 内层循环:生成列,1~(j <= i)
        for (int j = 1; j <= i; j++)
        {
            // 生成算式
            printf("%dx%d=%d\t", j, i, j*i);
            // printf("*"); // 替换成这行代码,输出下直角三角形
        }
        // 每一行所有列生成完毕,一定要换行
        printf("\n");
    }
    
    printf("\n");
    
    return 0;
}
案例:求100~200之间的所有素数

分析:

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

代码:

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

十、章节作业

(一)上机题

  1. 计算n以内所有正奇数的和,n值通过键盘输入。
  2. 计算1 + 1/(2 * 3) + 1/(3 * 4) + …+ 1/(n * (n + 1)),直到最后一项值小于0.00001为止。
  3. 计算1+1/2 - 1/3 + 1/4 - 1/5 …+1/n,n通过键盘输入(int k = -1)。
  4. 计算n的阶乘,n! = 1×2×3×…×n,n值通过键盘输入。
  5. 输出半径为1~10的圆面积,面积大于100时停止。
  6. 求输入的十个整数中正数的个数及其平均值。
  7. 打印出100以内能整除7之外的自然数。
  8. 打印乘法表。
  9. 我国古代数学家张丘建在《算经》一书中提出的数学问题:鸡翁一值钱五,鸡母一值钱三,鸡雏三值钱一。百钱买百鸡,问鸡翁、鸡母、鸡雏各几何?
  10. 从键盘上输入多个无符号整型数据,直到0结束,输出所输入数据中的最大值。

(二)思考题

  1. 判断一个数是不是回文数。(回文数是指正序(从左向右)和倒序(从右向左)读都是一样的整数,如:12321)
  2. 通过键盘录入一个整数,判断这个整数是否是水仙花数。

(三)扩展题

  1. 使用*在控制台分别打印上直角三角形、下直角三角形、上等腰三角形、下等腰三角形、菱形、平行四边形。

注: 后续笔记将围绕C语言知识展开,建议每日实操时长不少于3小时

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值