目录
条件控制的循环
循环次数事先未知的循环通常是由一个条件控制的,称为条件控制的循环。
此时用 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' 时退出游戏,输入其他字符时提示用户输入不合法并要求重新输入。
问题求解方法分析:
为了实现一个猜数游戏程序,我们需要考虑以下几个关键步骤和方法:
-
初始化和随机数生成:
- 使用 srand(time(NULL)) 初始化随机数生成器,以确保每次运行程序时生成的随机数不同。
- 在每轮游戏开始时,生成一个新的随机数 magic,范围在 1 到 100 之间。
-
猜数逻辑:
- 使用一个 do-while 循环来控制单次猜数过程,用户每次猜数后,程序会根据猜的结果给出反馈(太大、太小或猜对了)。
- 计数器 counter 用来记录用户猜数的次数,如果用户猜对了或者猜了 10 次仍未猜对,则结束本轮游戏。
-
输入验证:
- 在每次读取用户输入时,检查 scanf 的返回值 ret,以确保用户输入的是一个有效的整数。
- 如果 scanf 读取失败(ret != 1),则清除输入缓冲区中的非法字符,并提示用户重新输入。
-
用户选择是否继续:
- 每轮猜数结束后,使用一个 do-while 循环来询问用户是否继续游戏。
- 用户输入 'Y' 或 'y' 时,继续下一轮游戏。
- 用户输入 'N' 或 'n' 时,打印退出消息并结束程序。
- 用户输入其他字符时,提示用户输入不合法,并要求重新输入,直到输入合法字符为止。
-
程序结构:
- 外部的 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' 时退出程序,输入其他字符时提示用户输入不合法并要求重新输入。
问题求解方法分析:
为了实现一个计算器程序,用户可以进行多次数学运算,并且在每次运算结束后询问用户是否继续,我们需要考虑以下几个关键步骤和方法:
-
初始化和输入:
- 使用 scanf 读取用户输入的两个浮点数 data1 和 data2 以及运算符 op。
- 检查 scanf 的返回值 ret,确保读取了三个有效的值。如果 ret 不等于 3,说明输入有误,清除输入缓冲区并提示用户重新输入。
-
运算逻辑:
- 根据用户输入的运算符 op,使用 switch 语句执行相应的运算,并输出结果。
- 支持的运算符包括 +(加法)、-(减法)、*(乘法)、x(乘法)、X(乘法)、/(除法)和 ^(指数运算)。
- 对于除法运算,检查除数是否为零,如果是零则提示用户除数不能为零。
-
用户选择是否继续:
- 每次运算结束后,使用do-while 循环询问用户是否继续进行下一次运算。
- 用户输入 'Y' 或 'y' 时,继续下一轮运算。
- 用户输入 'N' 或 'n' 时,打印退出消息并结束程序。
- 用户输入其他字符时,提示用户输入不合法,并要求重新输入,直到输入合法字符为止。
-
程序结构:
- 外部的 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;
}
程序的运行结果如下所示: