Bootstrap

【第六章·循环控制结构】第四节:条件控制的循环

目录

条件控制的循环

示例:简单的猜数游戏

问题求解方法分析

设定 rand() 函数随机数范围

 示例:循环猜数游戏

问题求解方法分析

伪随机数

解决随机数重复:使用 srand() 设置种子

示例:限制次数的猜数游戏

示例:能处理非法输入并清空输入缓冲区的猜数游戏

问题求解方法分析

处理非法输入

清空输入缓冲区

示例:可选择继续下一轮的猜数游戏

示例:可选择退出的猜数游戏

示例:可选择继续或退出的猜数游戏

示例:可选择继续或退出的计算器程序


条件控制的循环

        循环次数事先未知的循环通常是由一个条件控制的,称为条件控制的循环

        此时用 while 语句和 do-while 语句编程更方便。

示例:简单的猜数游戏

        【例 6.6】编程设计一个简单的猜数游戏:先由计算机 “想” 一个数请用户猜,如果用户猜对了,则计算机给出提示 “Right!”,否则提示 “Wrong!”,并告诉用户所猜的数是大还是小。

问题求解方法分析

        本例程序设计的难点是如何让计算机 “想” 一个数。“想” 反映了一种随机性,可用随机函数 rand() 生成计算机 “想” 的数。

        由于只允许用户猜一次,因此采用多分支选择结构即可实现。算法设计如下:

  • Step 1 通过调用随机函数任意 “想” 一个数 magic。
  • Step 2 输入用户猜的数 guess。
  • Step 3 如果 guess 大于 magic,则给出提示:“Wrong! Too big!”。
  • Step 4 否则如果 guess 小于 magic,则给出提示:“Wrong! Too small!”。
  • Step 5 否则,即 guess 等于 magic,则给出提示:“Right!”。

        算法流程图如图 6-6 所示:

        程序如下:

#include <stdlib.h>  // 使用 rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic; // 计算机 “想” 的数
    int guess; // 用户猜的数

    magic = rand(); // 调用随机函数 “想” 一个数 magic
    
    printf("Please guess a magic number: ");
    scanf("%d", &guess); // 输入用户猜的数 guess

    if (guess > magic)
    {
        // 若 guess > magic,则提示 "Wrong! Too big!"
        printf("Wrong! Too big!\n");
    }
    else if (guess < magic)
    {
        // 若 guess < magic,则提示 "Wrong! Too small!"
        printf("Wrong! Too small!\n");
    }
    else
    {
        // 否则提示 “Right!”
        printf("Right!\n");
    }

    return 0;
}

        程序的 4 次测试结果如下:

设定 rand() 函数随机数范围

        由于随机函数 rand() 产生的是一个在 0 到 RAND_MAX 之间的整数,符号常量 RAND_MAX 是在头文件 stdlib.h 中定义的,因此使用该函数时需要包含头文件 stdlib.h。

        标准 C 规定 RAND_MAX 的值不大于双字节(16 位二进制)整数的最大值 32767(2^15 -1)。也就是说,程序第 9 行调用 rand() 生成的是一个在 0 到 32767 之间的整数。

        如果想改变计算机生成的随机数的取值范围,可以采用以下方法来控制计算机生成的随机数的取值范围:

  • 利用求余运算 rand() % b 将函数 rand() 生成的随机数变化在 [0, b-1]
  • 利用 rand() % b + a 将随机数的取值范围平移到 [a, a + b - 1] 上

        例如,要生成一个 1 到 100 之间的随机数,只要将第 9 行语句修改为如下语句即可:

magic = rand() % 100 + 1;
// rand() % 100 生成一个 0 到 99 之间的随机数,
// 加上 1 后范围就变成了 1 到 100。

        rand() 函数本身不能直接返回一个负数。如果要生成 -100 到 100 之间的随机数,可以这样做:

magic = rand() % 201 - 100;
// rand() % 201 生成一个 0 到 200 之间的随机数,
// 减去 100 后范围就变成了 -100 到 100。

 示例:循环猜数游戏

        【例 6.7】将例 6.6 的猜数游戏改为:每次猜数允许用户直到猜对为止,同时记录用户猜的次数,以此来反映用户 “猜” 数的水平。

问题求解方法分析

        由于用户猜多少次能猜对事先是未知的,即循环的次数未知,所以这是一个条件控制的循环,控制循环的条件是 “直到猜对为止”

        由于必须由用户先猜,然后才能知道有没有猜对,因此这是一个典型的直到型循环。所以本例特别适合用 do-while 语句来编程。

        算法的流程如图 6-7 所示:

        程序如下:

#include <stdlib.h> // 使用 rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic;       // 计算机 “想” 的数
    int guess;       // 用户猜的数
    int counter = 0; // 计数器,记录用户猜的次数,初始化为 0

    magic = rand() % 100 + 1; // 生成一个 1 到 100 之间的随机数

    do
    {
        printf("Please guess a magic number: ");
        scanf("%d", &guess); // 输入用户猜的数 guess

        counter++; // 猜一次数,计数器变量 counter 就加 1

        if (guess > magic)
        {
            printf("Wrong! Too big!\n");
        }
        else if (guess < magic)
        {
            printf("Wrong! Too small!\n");
        }
        else
        {
            printf("Right!\n");
        }
    } while (guess != magic); // 执行循环直到猜对为止

    printf("counter = %d\n", counter); // 打印用户猜数的次数 counter

    return 0;
}

        程序的 4 次测试结果如下:

伪随机数

        每次运行程序,计算机所 “想” 的数为什么都一样呢?

        原来用函数 rand() 所产生的随机数其实只是一个伪随机数,反复调用函数 rand() 所产生的一系列数看似是随机的,但每次执行程序时所产生的随机数序列却是一样的,都是相同的一个数列,而程序又每次只用到了数列中的第一个随机数

解决随机数重复:使用 srand() 设置种子

        解决这个问题的办法就是使程序每次运行时产生不同的随机数序列,由于不同的随机数序列中的第一个随机数是不同的,这样每次运行程序时,计算机所 “想” 的数就不一样了。

        产生这种随机数的过程称为“随机化”,它是通过调用函数 srand() 为函数 rand() 设置随机数种子来实现的。

        为了更好地理解这一点,先来看下面的程序:

#include <stdlib.h> // 使用 srand()、rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic, i;

    srand(1); // 设置随机数种子为 1

    for (i = 0; i < 10; i++)
    {
        magic = rand() % 100 + 1;
        printf("%d ", magic);
    }

    printf("\n");

    return 0;
}

        程序的运行结果如下所示:

        如果将第 8 行语句 srand(1); 改成 srand(2);,即将随机数种子由 1 改为 2,那么程序的运行结果将变为: 

        可见,只要设置的随机数种子不同,执行程序时就会产生不同的随机数序列

        按照此方法修改的猜数游戏程序如下:

#include <stdlib.h> // 使用 srand()、rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic, guess, counter = 0; // 计数器,记录用户猜的次数,初始化为 0
    unsigned int seed;             // 定义一个无符号整型变量

    printf("Please enter seed: "); // 提示输入随机数种子
    scanf("%u", &seed);            // 输入函数 srand() 所需要的参数

    srand(seed); // 为函数 rand() 设置随机数种子

    magic = rand() % 100 + 1; // 生成一个 1 到 100 之间的随机数

    do
    {
        printf("Please guess a magic number: ");
        scanf("%d", &guess); // 输入用户猜的数 guess
        counter++;           // 计数器变量 counter 加 1

        if (guess > magic)
        {
            printf("Wrong! Too big!\n");
        }
        else if (guess < magic)
        {
            printf("Wrong! Too small!\n");
        }
        else
        {
            printf("Right!\n");
        }
    } while (guess != magic); // 执行循环直到猜对为止

    printf("counter = %d\n", counter); // 打印用户猜数的次数 counter

    return 0;
}

        程序的 2 次测试结果如下:

        如果不希望每次都通过输入随机数种子来完成随机化,则可以使用下面的语句通过函数 time() 读取计算机的时钟值,并把该值设置为随机数种子。 

srand(time(NULL));

        函数 time() 返回以秒计算的日历时间,即从一个标准时间点到当前时刻经过的相对时间(单位为秒)

        使用 NULL 作为 time() 的参数时,time(NULL) 的返回值被转换为一个无符号整数,可作为随机数发生器的种子

        使用函数 time() 时,必须在程序开头将头文件 <time.h> 包含到程序中。 

        按此方法修改的程序如下:

#include <time.h>   // 将函数 time() 所需要的头文件 time.h 包含到程序中
#include <stdlib.h> // 使用 srand()、rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic, guess, counter = 0; // 计数器,记录用户猜的次数,初始化为 0

    srand(time(NULL)); // 为函数 rand() 设置随机数种子

    magic = rand() % 100 + 1; // 生成一个 1 到 100 之间的随机数

    do
    {
        printf("Please guess a magic number: ");
        scanf("%d", &guess); // 输入用户猜的数 guess

        counter++; // 计数器变量 counter 加 1

        if (guess > magic)
        {
            printf("Wrong! Too big!\n");
        }
        else if (guess < magic)
        {
            printf("Wrong! Too small!\n");
        }
        else
        {
            printf("Right!\n");
        }
    } while (guess != magic); // 执行循环直到猜对为止

    printf("counter = %d\n", counter); // 打印用户猜数的次数 counter

    return 0;
}

        通过这种方式,每次运行程序时,rand() 函数都会产生不同的随机数序列,确保了游戏的随机性和趣味性。 

示例:限制次数的猜数游戏

        【例 6.8】将例 6.7 的猜数游戏改为:每次猜数只允许用户最多猜 10 次,即用户猜对了或者猜了 10 次仍未猜对,都结束游戏。

问题求解方法分析:

        本例仍是一个条件控制的循环,但是控制循环的条件由 “直到猜对为止” 变为了 “最多猜 10 次”。也就是说,若猜对,则结束循环;否则看猜的次数是否超过 10 次,若未超过 10 次,则继续循环;若超过了 10 次,即使未猜对也结束循环。因此,本例只要在例 6.7 程序的基础上加强循环测试条件即可

        程序如下:

#include <time.h>   // 将函数 time() 所需要的头文件 time.h 包含到程序中
#include <stdlib.h> // 使用 srand()、rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic, guess, counter = 0;

    srand(time(NULL));        // 使用当前时间作为随机数种子
    magic = rand() % 100 + 1; // 生成一个 1 到 100 之间的随机数

    do
    {
        printf("Please guess a magic number: ");
        scanf("%d", &guess); // 输入用户猜的数
        counter++;           // 计数器变量 counter 加 1

        if (guess > magic)
        {
            printf("Wrong! Too big!\n");
        }
        else if (guess < magic)
        {
            printf("Wrong! Too small!\n");
        }
        else
        {
            printf("Right!\n");
        }
    } while (guess != magic && counter < 10); // 猜不对且未超过 10 次时继续猜

    printf("counter = %d\n", counter); // 打印用户猜数的次数
    printf("游戏结束,欢迎下次游玩^_^");

    return 0;
}

        程序的运行结果如下所示:

示例:能处理非法输入并清空输入缓冲区的猜数游戏

        【例 6.9】修改例 6.8 程序,增强程序的健壮性,使其具有遇到不正确使用或非法数据输入时避免出错的能力。

问题求解方法分析

        用函数 scanf() 输入用户猜测的数据时,若用户不慎输入了非数字字符,则程序运行就会出错。这是因为 scanf() 按指定格式读取输入缓冲区中的数据,如果读取失败,则缓冲区中的非数字字符不会被读走,仍然留在缓冲区中,这样程序中后面的 scanf() 调用就都是在读取这个非数字字符,不断地读取,不断地失败。

处理非法输入

        如第 5 章 5.10 节中所述,若函数 scanf() 调用成功,则其返回值为已成功读入的数据项数。通常非法字符的输入会导致数据不能成功读入。例如,要求输入的数据是数字,而用户输入的是字符。因此,通过检验函数 scanf() 返回值,可以确认用户是否不慎输入了非法字符,但例 5.10 只能打印出错误提示信息并结束程序的运行。

清空输入缓冲区

        若要增强程序的健壮性,仅仅打印出错误提示信息是不够的,必须能够让用户重新输入数据,直到输入了正确的数据为止。当然,在用户重新输入数据之前,还需先清除留存于输入缓冲区中的非数字字符,才能保证后续输入的数据能被正确地读入

        可使用前面提过的两种方法:

  • 使用 fflush(stdin) 清空输入缓冲区,但在标准 C 中并不推荐使用
  • 使用 while ((c = getchar()) != '\n' && c != EOF) 清空输入缓冲区)

提示

        关于输入非法字符的检查处理及清空输入缓冲区的知识点,我已在一篇博客中详细阐述,欢迎参考:【第五章·选择控制结构】第十节:本章扩充内容 2(对输入非法字符的检查与处理)

        程序如下:

#include <time.h>   // 将函数 time() 所需要的头文件 time.h 包含到程序中
#include <stdlib.h> // 使用 srand()、rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic, guess, counter = 0;
    int ret; // 用于保存函数 scanf() 的返回值

    srand(time(NULL));        // 使用当前时间作为随机数种子
    magic = rand() % 100 + 1; // 生成一个 1 到 100 之间的随机数

    do
    {
        printf("Please guess a magic number: ");
        ret = scanf("%d", &guess); // 输入用户猜的数

        // 内嵌一个 while 循环,用于判断 scanf 的返回值是否正确
        while (ret != 1) // 若存在输入错误,则重新输入
        {
            // 清除输入缓冲区中的非法字符
            // 也可以使用 fflush 函数,但是标准 C 中并不推荐使用
            while (getchar() != '\n')
                ;
            // 更加细致且全面的做法是:
            // int c;
            // while ((c = getchar()) != '\n' && c != EOF);

            // 要求用户重新输入数据
            printf("Please guess a magic number: ");
            ret = scanf("%d", &guess);
        }

        counter++; // 计数器变量 counter 加 1

        if (guess > magic)
        {
            printf("Wrong! Too big!\n");
        }
        else if (guess < magic)
        {
            printf("Wrong! Too small!\n");
        }
        else
        {
            printf("Right!\n");
        }
    } while (guess != magic && counter < 10); // 猜不对且未超过 10 次时继续猜

    printf("counter = %d\n", counter); // 打印用户猜数的次数

    return 0;
}

        程序的运行结果如下所示:

        关于本例中出现的 while (getchar() != '\n'); 语句的作用是:读取并丢弃输入缓冲区中的字符,直到遇到换行符(\n)为止(第一个换行符(\n)也会被读取并丢弃)。它通常用于处理 scanf 等输入函数后留在缓冲区中的多余字符,防止这些字符干扰后续的输入操作。

        这是一种简单的清空输入缓冲区的方法,但需要注意其局限性。特别是当输入流中可能包含多个连续的换行符或其他特殊字符时,可能需要更复杂的逻辑来确保所有不需要的字符都被正确清理

        此外,在某些编译器或平台上,使用 fflush(stdin) 来清空输入缓冲区可能是一个更直接但非标准的方法(fflush 的行为对于输入流是未定义的,但在某些实现中它确实可以工作)。然而,由于可移植性和标准合规性的原因,通常建议避免使用 fflush(stdin)

 提示:

        再实际编程中,我们常使用如下语句来清理输入缓冲区:

int c;
while ((c = getchar()) != '\n' && c != EOF)
    ;

        这种方法通过一个 while 循环读取并丢弃所有字符,直到遇到回车符 \n 或文件结束符 EOF。这可以确保输入缓冲区中的所有多余字符都被清除,并且代码更加健壮。

        这个循环的工作原理是:

  • getchar() 从标准输入读取一个字符,并将其赋值给变量 c(最好是 int 类型,因为要存储 EOF(-1) 这个值)。
  • 检查 c 是否等于换行符(\n)或文件结束符(EOF)。
  • 如果 c 不是换行符且不是 EOF,循环继续执行,再次调用 getchar()。
  • 一旦 c 是换行符或 EOF,循环终止即遇到第一个换行符(\n)或文件结束符(EOF),会读取并丢弃它,然后立刻结束循环

示例:可选择继续下一轮的猜数游戏

        【例 6.10】将例 6.9 改为:每次运行程序可以猜多个数,每个数最多可猜 10 次,若 10 次仍未猜对,则停止本次猜数,给出如下提示信息,询问用户是否继续猜数:

Do you want to continue (Y/N or y/n)?

        若用户输入 'Y' 或 'y',则继续猜下一个数;否则结束程序的执行。

问题求解方法分析:

        修改例 6.9 程序,在 do-while 循环外面再增加一个 do-while 循环,用于控制猜多个数。在循环体的开始处让计算机重新 “想” 一个数,在循环体的最后询问用户是否继续。若用户回答 'Y' 或 'y',则循环继续;否则程序结束。

        程序如下:

#include <time.h>   // 将函数 time() 所需要的头文件 time.h 包含到程序中
#include <stdlib.h> // 使用 srand()、rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic, guess, counter = 0, ret;
    char reply; // 保存用户输入的回答

    srand(time(NULL)); // 使用当前时间作为随机数种子

    do
    {
        counter = 0;              // 猜下一个数之前,将计数器清零
        magic = rand() % 100 + 1; // 生成一个 1 到 100 之间的随机数

        do
        {
            printf("Please guess a magic number: ");
            ret = scanf("%d", &guess); // 输入用户猜的数

            while (ret != 1) // 若存在输入错误,则重新输入
            {
                // 清除输入缓冲区中的非法字符
                while (getchar() != '\n')
                    ;

                // 要求用户重新输入数据
                printf("Please guess a magic number: ");
                ret = scanf("%d", &guess);
            }

            counter++; // 计数器变量 counter 加 1

            if (guess > magic)
            {
                printf("Wrong! Too big!\n");
            }
            else if (guess < magic)
            {
                printf("Wrong! Too small!\n");
            }
            else
            {
                printf("Right!\n");
            }
        } while (guess != magic && counter < 10); // 猜不对且未超过 10 次时继续猜

        printf("counter = %d\n", counter); // 打印用户猜数的次数

        printf("Do you want to continue (Y/N or y/n)? "); // 提示是否继续
        scanf(" %c", &reply);                             // %c 前有一个空格,用于忽略前一个输入的换行符

    } while (reply == 'Y' || reply == 'y'); // 输入 Y 或 y 则程序继续

    printf("游戏结束,欢迎下次游玩^_^");

    return 0;
}

        程序的运行结果如下所示:

        这个程序使用了三重循环:

  • 最外层的 do-while 循环控制猜多个数,直到用户想停止时为止;
  • 第二层的 do-while 循环控制猜一个数的过程,直到猜对或者猜的次数超过 10 次为止;
  • 最内层的 while 循环确保用户每次从键盘输入的数都是合法的数字字符。

        第 52 行语句在 %c 前加空格的作用是避免前面第 20 行或第 30 行数据输入时存入输入缓冲区中的回车符被第 52 行语句作为有效字符读给字符型变量 reply。详见第 4 章 4.4 节的介绍。 

示例:可选择退出的猜数游戏

        运行上述程序(例 6.10)时,只要用户输入'Y' 或 'y' 以外的字符,程序都会结束,如果希望用户仅在输入 'N' 或 'n' 时才结束程序,输入 'N' 或 'n' 以外的字符都继续猜数,那么应该如何修改程序 do-while 循环的控制条件呢?

问题求解方法分析:

        可以修改 do-while 循环的控制条件,使其在用户输入 'N' 或 'n' 时退出循环,其他情况下继续循环。

        需要修改的代码如下:

} while (reply != 'N' && reply != 'n');  // 输入 N 或 n 则程序结束

        完整程序如下:

#include <time.h>   // 将函数 time() 所需要的头文件 time.h 包含到程序中
#include <stdlib.h> // 使用 srand()、rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic, guess, counter = 0, ret;
    char reply; // 保存用户输入的回答

    srand(time(NULL)); // 使用当前时间作为随机数种子

    do
    {
        counter = 0;              // 猜下一个数之前,将计数器清零
        magic = rand() % 100 + 1; // 生成一个 1 到 100 之间的随机数

        do
        {
            printf("Please guess a magic number: ");
            ret = scanf("%d", &guess); // 输入用户猜的数

            while (ret != 1) // 若存在输入错误,则重新输入
            {
                // 清除输入缓冲区中的非法字符
                while (getchar() != '\n')
                    ;

                // 要求用户重新输入数据
                printf("Please guess a magic number: ");
                ret = scanf("%d", &guess);
            }

            counter++; // 计数器变量 counter 加 1

            if (guess > magic)
            {
                printf("Wrong! Too big!\n");
            }
            else if (guess < magic)
            {
                printf("Wrong! Too small!\n");
            }
            else
            {
                printf("Right!\n");
            }
        } while (guess != magic && counter < 10); // 猜不对且未超过 10 次时继续猜

        printf("counter = %d\n", counter); // 打印用户猜数的次数

        printf("Do you want to continue (Y/N or y/n)? "); // 提示是否继续
        scanf(" %c", &reply);                             // %c 前有一个空格,用于忽略前一个输入的换行符

    } while (reply != 'N' && reply != 'n'); // 输入 N 或 n 则程序结束

    printf("游戏结束,欢迎下次游玩^_^");

    return 0;
}

        程序的运行结果如下所示:

示例:可选择继续或退出的猜数游戏

        现在需要实现一个猜数游戏程序,用户可以多次进行猜数游戏,每次猜数最多允许猜10次。每次猜数结束后,程序会询问用户是否继续游戏。用户输入 'Y' 或 'y' 时继续下一轮游戏,输入 'N' 或 'n' 时退出游戏,输入其他字符时提示用户输入不合法并要求重新输入。

问题求解方法分析:

        为了实现一个猜数游戏程序,我们需要考虑以下几个关键步骤和方法:

  1. 初始化和随机数生成

    • 使用 srand(time(NULL)) 初始化随机数生成器,以确保每次运行程序时生成的随机数不同。
    • 在每轮游戏开始时,生成一个新的随机数 magic,范围在 1 到 100 之间。
  2. 猜数逻辑

    • 使用一个 do-while 循环来控制单次猜数过程,用户每次猜数后,程序会根据猜的结果给出反馈(太大、太小或猜对了)。
    • 计数器 counter 用来记录用户猜数的次数,如果用户猜对了或者猜了 10 次仍未猜对,则结束本轮游戏。
  3. 输入验证

    • 在每次读取用户输入时,检查 scanf 的返回值 ret,以确保用户输入的是一个有效的整数。
    • 如果 scanf 读取失败(ret != 1),则清除输入缓冲区中的非法字符,并提示用户重新输入。
  4. 用户选择是否继续

    • 每轮猜数结束后,使用一个 do-while 循环来询问用户是否继续游戏
    • 用户输入 'Y' 或 'y' 时,继续下一轮游戏。
    • 用户输入 'N' 或 'n' 时,打印退出消息并结束程序。
    • 用户输入其他字符时,提示用户输入不合法,并要求重新输入,直到输入合法字符为止。
  5. 程序结构

    • 外部的 do-while 循环控制多轮游戏的进行。
    • 内部的 do-while 循环控制单次猜数过程。
    • 另一个 do-while 循环用于处理用户是否继续游戏的选择。

        程序如下:

#include <time.h>   // 将函数 time() 所需要的头文件 time.h 包含到程序中
#include <stdlib.h> // 使用 srand()、rand() 随机数函数
#include <stdio.h>

int main(void)
{
    int magic, guess, counter = 0, ret;
    char reply; // 保存用户输入的回答

    srand(time(NULL)); // 使用当前时间作为随机数种子

    do
    {
        counter = 0;              // 猜下一个数之前,将计数器清零
        magic = rand() % 100 + 1; // 生成一个 1 到 100 之间的随机数

        do
        {
            printf("Please guess a magic number: ");
            ret = scanf("%d", &guess); // 输入用户猜的数

            // 若存在输入错误,则重新输入
            while (ret != 1)
            {
                // 清除输入缓冲区中的非法字符
                while (getchar() != '\n')
                    ;

                // 要求用户重新输入数据
                printf("Please guess a magic number: ");
                ret = scanf("%d", &guess);
            }

            counter++; // 计数器变量 counter 加 1

            if (guess > magic)
            {
                printf("Wrong! Too big!\n");
            }
            else if (guess < magic)
            {
                printf("Wrong! Too small!\n");
            }
            else
            {
                printf("Right!\n");
            }
        } while (guess != magic && counter < 10); // 猜不对且未超过 10 次时继续猜

        printf("counter = %d\n", counter); // 打印用户猜数的次数

        // 询问用户是否继续
        do
        {
            printf("Do you want to continue (Y/N or y/n)? "); // 提示是否继续
            scanf(" %c", &reply);                             // %c 前有一个空格,用于忽略前一个输入的换行符

            if (reply == 'Y' || reply == 'y')
            {
                // 继续下一轮
                break;
            }
            else if (reply == 'N' || reply == 'n')
            {
                // 退出游戏
                printf("游戏结束,欢迎下次游玩^_^\n");
                return 0;
            }
            else
            {
                // 输入不合法,提示用户重新输入
                printf("你输入的字符不合法,请重新输入一个字符。\n");
            }
        } while (1); // 无限循环直到输入合法字符

    } while (1); // 无限循环直到用户选择退出

    return 0;
}

        程序的运行结果如下所示:

示例:可选择继续或退出的计算器程序

        现在需要实现一个计算器程序,用户可以进行多次数学运算,包括加法、减法、乘法、除法和指数运算。每次运算结束后,程序会询问用户是否继续进行下一次运算。用户输入 'Y' 或 'y' 时继续下一轮运算,输入 'N' 或 'n' 时退出程序,输入其他字符时提示用户输入不合法并要求重新输入。

问题求解方法分析:

        为了实现一个计算器程序,用户可以进行多次数学运算,并且在每次运算结束后询问用户是否继续,我们需要考虑以下几个关键步骤和方法:

  1. 初始化和输入

    • 使用 scanf 读取用户输入的两个浮点数 data1 和 data2 以及运算符 op。
    • 检查 scanf 的返回值 ret,确保读取了三个有效的值。如果 ret 不等于 3,说明输入有误,清除输入缓冲区并提示用户重新输入。
  2. 运算逻辑

    • 根据用户输入的运算符 op,使用 switch 语句执行相应的运算,并输出结果。
    • 支持的运算符包括 +(加法)、-(减法)、*(乘法)、x(乘法)、X(乘法)、/(除法)和 ^(指数运算)。
    • 对于除法运算,检查除数是否为零,如果是零则提示用户除数不能为零。
  3. 用户选择是否继续

    • 每次运算结束后,使用do-while 循环询问用户是否继续进行下一次运算
    • 用户输入 'Y' 或 'y' 时,继续下一轮运算。
    • 用户输入 'N' 或 'n' 时,打印退出消息并结束程序。
    • 用户输入其他字符时,提示用户输入不合法,并要求重新输入,直到输入合法字符为止。
  4. 程序结构

    • 外部的 do-while 循环控制多轮运算的进行。
    • 内部的 do-while 循环用于处理用户是否继续的选择,确保用户输入合法字符。

        程序如下:

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

#define EPS 1e-7

int main(void)
{
    float data1, data2;
    char op, reply; // 保存用户输入的回答
    int ret;        // 用于保存 scanf 的返回值

    do
    {
        printf("请输入一个表达式: ");
        ret = scanf("%f %c %f", &data1, &op, &data2); // 输入运算表达式

        // 检查 scanf 的返回值,确保正确读取了三个值
        if (ret != 3)
        {
            // 清除输入缓冲区中的非法字符
            while (getchar() != '\n')
                ;
            printf("输入错误,请重新输入!\n");
            continue; // 跳过本次循环
        }

        // 根据输入的运算符确定执行的运算
        switch (op)
        {
        case '+': // 加法运算
            printf("%.2f + %.2f = %.2f\n", data1, data2, data1 + data2);
            break;
        case '-': // 减法运算
            printf("%.2f - %.2f = %.2f\n", data1, data2, data1 - data2);
            break;
        case '*': // 乘法运算
        case 'x': // 乘法运算
        case 'X': // 乘法运算
            printf("%.2f * %.2f = %.2f\n", data1, data2, data1 * data2);
            break;
        case '/':                   // 除法运算
            if (fabs(data2) <= EPS) // 实数与 0 比较
            {
                printf("除数不能为零!\n");
            }
            else
            {
                printf("%.2f / %.2f = %.2f\n", data1, data2, data1 / data2);
            }
            break;
        case '^': // 指数运算
            printf("%.2f ^ %.2f = %.2f\n", data1, data2, pow(data1, data2));
            break;
        default: // 处理非法运算符
            printf("无效的运算符!\n");
        }

        // 询问用户是否继续
        do
        {
            printf("Do you want to continue (Y/N or y/n)? "); // 提示是否继续
            ret = scanf(" %c", &reply);                       // %c 前有一个空格,用于忽略前一个输入的换行符

            // 检查 scanf 的返回值,确保正确读取了字符
            if (ret != 1)
            {
                // 清除输入缓冲区中的非法字符
                while (getchar() != '\n')
                    ;
                printf("输入错误,请重新输入!\n");
                continue; // 跳过本次循环
            }

            if (reply == 'Y' || reply == 'y')
            {
                // 继续下一轮
                break; // 结束本次循环
            }
            else if (reply == 'N' || reply == 'n')
            {
                // 退出程序
                printf("程序结束,感谢使用!\n");
                return 0;
            }
            else
            {
                // 输入不合法,提示用户重新输入
                printf("你输入的字符不合法,请重新输入一个字符。\n");
            }
        } while (1); // 无限循环直到输入合法字符

    } while (1); // 无限循环直到用户选择退出

    return 0;
}

        程序的运行结果如下所示:

;