zhyDaDa的个人站点

* 对应课程: 4.1.1\~5.3.3
目录


while 循环

while (condition) {
    code; // 循环体
}

不多解释, 更详细的请看这篇↓
Day09_while和break

do-while 循环

do{
    code; // 循环体
} while (condition);

该结构进入循环的时候不做检查, 即, 代码至少运行一次
注意: 由于圆括号()表示的是语句, 因此在最后一定要记得加上分号;

例子 求对数

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

int main()
{
    int x;
    int ret = 0;

    printf("The num ?\n");
    scanf("%d", &x);

    int t = x;
    while (x > 1)
    {
        x /= 2;
        ret++;
    }
    printf("log2 of %d is %d", t, ret);

    if (pow(2, ret) != t)
    {
        printf("(approximately)");
    }

    return 0;
}

小套路: x在参与了运算后改变了, 先用一个t来备份

灵魂三问

  1. 循环要执行几次?
  2. 循环停止时输出了什么?
  3. 循环结束后, 变量的值是多少?

例1 猜数游戏

计算机想一个数, 告诉玩家的猜测是大还是小, 直到猜中, 最后告诉用户猜了多少次
思路:
+ 随机数, 存入number
+ 一个负责记次数的变量count初始化为零
+ 让用户输入一个数字a
+ count递增
+ 判断两者关系, 并输出”大/小”
+ 如果不相等返回第三步
+ 否则输出”猜中”次数

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main()
{
    srand(time(0));
    int number = rand() % 100 + 1;
    int count;
    int a = 0;

    printf("我已经想好了一个1到100以内的整数\n");
    do
    {
        printf("请猜这个1到100之间的整数: ");
        scanf("%d", &a);
        count++;
        if (a > number)
            printf("你猜的数偏大\n");
        else if (a < number)
            printf("你猜的数偏小\n");
    } while (a != number);
    printf("答案正是%d, 你一共猜了%d次\n", number, count);

    return 0;
}

注释:
+ srand(time(0));的作用是初始化随机数, 具体原理不多解释
简单来说可以理解为 让随机数有随机数的样子
+ 调用rand();会返回一个随机的整数, 注意区别于其他语言

例2 算平均数

输入一系列正整数, 最后输入-1表示结束, 输出平均数

#include <stdio.h>

int main()
{
    int number;
    int sum = 0;
    int count = 0;

    printf("计算平均数\n输入一系列正整数, 最后输入-1表示结束\n");
    scanf("%d", &number);
    while (number != -1)
    {
        sum += number;
        count++;
        scanf("%d", &number);
    }

    printf("平均数为: %.1f\n", 1.0 * sum / count);
    return 0;
}

例3 对多位数的逆序排列

#include <stdio.h>

int main()
{
    int x1 = 0, x2 = 0, digit = 0, ans = 0;
    printf("\n输入一个多位正整数: ");
    scanf("%d", &x1);
    x2 = x1;

    /* 方法一: 逐位输出 */
    printf("\n方法一的结果: ");
    while (x1 > 0)
    {
        digit = x1 % 10;
        x1 /= 10;
        printf("%d", digit);
    }

    /* 方法二: 获得转换后的数字再输出 */
    while (x2 > 0)
    {
        digit = x2 % 10;
        x2 /= 10;
        ans = ans * 10 + digit;
    }
    printf("\n方法二的结果: %d", ans);

    return 0;
}

输出结果如下:

输入一个多位正整数: 700

方法一的结果: 007
方法二的结果: 7

for 循环

引入: 要算阶乘 n!
此时要有一个计数器, 特点是:
开始时要声明, 每次要判断, 最后还要做++运算

for 的语法

for(count=0; count<max; count++)

作为理解, 可以把 for 读作 对于, 比如:

对于一开始的count=0, 当count<max时, 循环一次, 在每轮循环完成后, 使得count++

另外, 由于 计数器 在循环外没用, 因此可以在循环中定义ta

注意: for 会在循环一开始判断循环条件
这一点和 while 是类似的

以下是求阶乘的代码 (做了优化处理)

#include <stdio.h>

int main()
{
    int n;
    printf("输入要求阶乘的正整数: ");
    scanf("%d", &n);
    int i = n;
    int ans = 1;

    for (; n > 1; n--)
    {
        ans *= n;
    }

    printf("%d 的阶乘为: %d\n", i, ans);
    return 0;
}

该程序以巧妙的方式, 考虑到了n01的情况

例子 求素数

#include <stdio.h>

int main()
{
    int x;
    printf("判断素数: ");
    scanf("%d", &x);

    int i;
    int isPrime = 1;
    for (i = 2; i < x / 2; i++)
    {
        if (x % i == 0)
        {
            isPrime = 0;
            break;
        }
    }
    if (isPrime == 1)
    {
        printf("是素数\n");
    }
    else
    {
        printf("不是素数\n");
    }
    return 0;
}

这里的 break 用于跳出循环
还有一个关键字 continue 用于进入下一轮循环

循环嵌套

获取1\~100内所有素数

#include <stdio.h>

int main()
{
    int x;
    for (x = 2; x <= 100; x++)
    {
        int i;
        int isPrime = 1;
        for (i = 2; i <= x / 2; i++)
        {
            if (x % i == 0)
            {
                isPrime = 0;
                break;
            }
        }
        if (isPrime == 1)
        {
            printf("%3d", x);
        }
    }
    return 0;
}

获取前50个素数

#include <stdio.h>

int main()
{
    int x;
    int count = 0;
    for (x = 2; count < 50; x++)
    {
        int i;
        int isPrime = 1;
        for (i = 2; i < x / 2 + 1; i++)
        {
            if (x % i == 0)
            {
                isPrime = 0;
                break;
            }
        }
        if (isPrime == 1)
        {
            count++;
            printf("%d\t", x);
            if (count % 5 == 0)
            {
                printf("\n");
            }
        }
    }
    return 0;
}

值得注意的是, 本段程序的 for 并未使用计数器 x 作为判断标志
应当把 for 看作是 while简写形式, 不应当带有stereotype

跳出嵌套

凑硬币

#include <stdio.h>

int main()
{
    int x;
    int one, two, five;

    printf("要凑几块钱? ");
    scanf("%d", &x);

    for (one = 0; one <= x * 10; one++)
    {
        for (two = 0; two <= x * 10 / 2; two++)
        {
            for (five = 0; five <= x * 10 / 5; five++)
            {
                if (one + two * 2 + five * 5 == x * 10)
                {
                    printf("可以用% 3d个一角 加% 3d个二角 加% 3d个五角 得到%d元\n", one, two, five, x);
                }
            }
        }
    }

    return 0;
}

如果只要求得到一种情况就直接跳出三个循环呢?

方法一 接力break

#include <stdio.h>

int main()
{
    int x;
    int one, two, five;
    int exit = 0;

    printf("要凑几块钱? ");
    scanf("%d", &x);

    for (one = 0; one <= x * 10; one++)
    {
        for (two = 0; two <= x * 10 / 2; two++)
        {
            for (five = 0; five <= x * 10 / 5; five++)
            {
                if (one + two * 2 + five * 5 == x * 10)
                {
                    printf("可以用% 3d个一角 加% 3d个二角 加% 3d个五角 得到%d元\n", one, two, five, x);
                    exit = 1;
                    break;
                }
            }
            if (exit == 1)
                break;
        }
        if (exit == 1)
            break;
    }

    return 0;
}

方法二 goto

#include <stdio.h>

int main()
{
    int x;
    int one, two, five;
    int exit = 0;

    printf("要凑几块钱? ");
    scanf("%d", &x);

    for (one = 0; one <= x * 10; one++)
    {
        for (two = 0; two <= x * 10 / 2; two++)
        {
            for (five = 0; five <= x * 10 / 5; five++)
            {
                if (one + two * 2 + five * 5 == x * 10)
                {
                    printf("可以用% 3d个一角 加% 3d个二角 加% 3d个五角 得到%d元\n", one, two, five, x);
                    goto out;
                }
            }
        }
    }
out:
    return 0;
}

goto 受人诟病的原因在于, ta破坏了程序的结构性
但在上述的例子中, 其优点是非常显著的
因此, 仅仅在合适的场合使用goto, 其他地方尽量不使用

例题 数学题

求$f(n)=\sum \limits _{i=1}^1 \frac{1}{n}$

#include <stdio.h>

int main()
{
    int n;
    int i;
    double sum = 0.0;
    printf("n: ");
    scanf("%d", &n);
    for (i = 1; i <= n; i++)
    {
        sum += 1.0 / i;
    }
    printf("f(%d)=%f", n, sum);

    return 0;
}

求$f(n)=\sum \limits _{i=1}^1 \frac{1}{n}\cdot(-1)^{n+1}$

#include <stdio.h>

int main()
{
    int n;
    int i;
    double sign = 1.0;
    double sum = 0.0;
    printf("n: ");
    scanf("%d", &n);
    for (i = 1; i <= n; i++)
    {
        sum += sign / i;
        sign *= -1;
    }
    printf("f(%d)=%f", n, sum);

    return 0;
}

例题 正序分解正整数

输入非负整数, 正序输出每一位数字
+ 输入: 13425
+ 输出: 1 3 4 2 5

注意: 最后的数字后面没有空格

#include <stdio.h>

int main()
{
    int x;
    int mask=1;
    printf("请输入非负整数: ");
    scanf("%d", &x);

    /* 先获得数位 */
    int t = x;
    while (t > 9)
    {
        t /= 10;
        mask *= 10;
    } 

    /* 再逐个输出 */
    do{
        int d = x / mask;
        printf("%d", d);
        if(mask>9)
            printf(" ");
        x %= mask;
        mask /= 10;
    } while (mask > 0);
    printf("|<-这是数字的末尾\n");
    return 0;
}

本题综合了循环的多个技巧, 实属佳题
尤其要琢磨循环的条件是什么

例题 求最大公约数

输入两个数a&b, 输出其最大公约数
+ 输入: 12 18
+ 输出: 6

法一 朴素解法

#include <stdio.h>

int main()
{
    int a, b;
    printf("输入两个整数: \n");
    scanf("%d %d", &a, &b);

    int ans = 0;
    int i;
    for (i = 1; i <= a && i <= b; i++)
    {
        if (a % i == 0 && b % i == 0)
        {
            ans = i;
        }
    }

    printf("最大公约数是: %d\n", ans);
    return 0;
}

这里用到了逻辑运算, 在这个课程中还没教, 不过不算难
可以看C#课程中相关的知识点: Day06_Convert类型转换_加加减减_关系运算与逻辑判断

法二 辗转相除法

辗转相除法定义:
1. 如果b等于0, 计算结束, a就是最大公约数
2. 否则, 计算a除以b余数, 再让ab互换
3. 返回第一步

#include <stdio.h>

int main()
{
    int a, b;
    int t;
    printf("输入两个整数: \n");
    scanf("%d %d", &a, &b);

    while (b!=0)
    {
        t = a % b;
        a = b;
        b = t;
    }   

    printf("最大公约数是: %d\n", a);
    return 0;
}
Avatar photo
我是 zhyDaDa

前端/UI/交互/独立游戏/JPOP/电吉他/游戏配乐/网球/纸牌魔术

发表回复