一个死循环的根本原因分析及解决办法
测 试 三月 24th, 2009下面这段程序,假入输入数字回车,比如“1↓”,则正常;假入输入一个字母回车,比如“a↓”,则会陷入死循环。
1 /* foo.c */
2 #include
3
4 #define MIN 1
5 #define MAX 100
6
7 int main(void)
8 {
9 int i;
10
11 while (1) {
12 printf(”\rPlz input an interger number (between %d and %d): \n”, MIN, MAX);
13 scanf(”%d”, &i);
14
15 if (i < MIN || i > MAX) {
16 printf(”\rERROR: Your choice must be between %d to %d!\n”, MIN, MAX);
17 continue;
18 } else {
19 break;
20 }
21 }
22 printf(”\rINFO: Your choice is %d!\n”, i);
23
24 return 0;
25 }
2 #include
3
4 #define MIN 1
5 #define MAX 100
6
7 int main(void)
8 {
9 int i;
10
11 while (1) {
12 printf(”\rPlz input an interger number (between %d and %d): \n”, MIN, MAX);
13 scanf(”%d”, &i);
14
15 if (i < MIN || i > MAX) {
16 printf(”\rERROR: Your choice must be between %d to %d!\n”, MIN, MAX);
17 continue;
18 } else {
19 break;
20 }
21 }
22 printf(”\rINFO: Your choice is %d!\n”, i);
23
24 return 0;
25 }
foo.c源代码
原因分析:
一、scanf返回值是多少?为什么?
scanf是有返回值的,返回值为成功读取的输入项的数目。跟踪可以发现,scanf返回值是0,而正常情况应该是1,这说明scanf失败。这是因为scanf对读取项的期望是整数,而输入的是字母字符,所以失败。
二、scanf失败后变量i的值是什么?
变量i定义时没有初始化,scanf失败,所以也就没有给i赋值,i值是不确定的,我这儿 i = 134514185。
三、死循环是怎么形成的?
scanf是从输入缓存取数据:
1. 当缓存里无数据,命令行阻塞,你输入直到回车,输入的数据都放到缓存里面;
2. 此时scanf才去缓存里面取数据,并做类型转换;
3. 如果scanf失败,在很多实现下,缓存的数据仍然在那里,这样执行到下一次scanf调用的时候,因为缓存里有数据,所以并不需要阻塞读取命令行输入,而是从已经有的缓存里面去取,所以就陷入了不需要输入的scanf不停读取->失败->再读取的死循环。
解决办法:
判断scanf返回值,对失败情况(不为1)进行出错处理,包括清理缓存。
代码改正如下:
1 /* bar.c */
2 #include
3
4 #define MIN 1
5 #define MAX 100
6
7 int main(void)
8 {
9 int i;
10 int ret = 0;
11
12 while (1) {
13 printf(”\rPlz input an interger number (between %d and %d): \n”, MIN, MAX);
14 ret = scanf(”%d”, &i);
15 if (ret != 1) {
16 getchar();
17 }
18
19 if (i < MIN || i > MAX) {
20 printf(”\rERROR: Your choice must be between %d to %d!\n”, MIN, MAX);
21 continue;
22 } else {
23 break;
24 }
25 }
26 printf(”\rINFO: Your choice is %d!\n”, i);
27
28 return 0;
29 }
2 #include
3
4 #define MIN 1
5 #define MAX 100
6
7 int main(void)
8 {
9 int i;
10 int ret = 0;
11
12 while (1) {
13 printf(”\rPlz input an interger number (between %d and %d): \n”, MIN, MAX);
14 ret = scanf(”%d”, &i);
15 if (ret != 1) {
16 getchar();
17 }
18
19 if (i < MIN || i > MAX) {
20 printf(”\rERROR: Your choice must be between %d to %d!\n”, MIN, MAX);
21 continue;
22 } else {
23 break;
24 }
25 }
26 printf(”\rINFO: Your choice is %d!\n”, i);
27
28 return 0;
29 }
就是在scanf出错时使用getchar()来读取stdin的内容从而实现缓存的清空。
注意:
1、getchar()一次只读取一个字符;
2、不能使用fflush(stdin)来清空缓存,根据C99标准,fflush是用来刷新输出流的,输入流使用fflush的行为是未定义。
最近评论