下面是一段实现两个数加减乘除的简易计算器代码,由此发现了一些以前没有注意到的问题.
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int main(int argc, char* argv[]) 6 { 7 double firstNum, secondNum; 8 double result = 0; 9 int operatoring; 10 11 char buff1[10]; 12 char buff2[10]; 13 14 printf("please input the first number:\n"); 15 firstNum = atof(gets(buff1)); 16 17 printf("please input the operator: +, -, *, /\n"); 18 operatoring = getchar(); 19 // while((operatoring = getchar()) != '\n' && operatoring != EOF); 20 // fflush(stdin); 21 // printf("%d\n", operatoring); 22 23 printf("please input the second number:\n"); 24 secondNum = atof(gets(buff2)); 25 26 switch (operatoring) 27 { 28 case 0x2B : 29 result = firstNum + secondNum; 30 break; 31 case 0x2D : 32 result = firstNum - secondNum; 33 break; 34 case 0x2A : 35 result = firstNum * secondNum; 36 break; 37 case 0x2F : 38 result = firstNum / secondNum; 39 break; 40 } 41 printf("result = %f\n", result); 42 43 return 0; 44 }
char* gets(char* str) ------ 从标准输入(stdin)读入字符串并存入str,直到遇到换行符或者文件结束符(EOF),返回 str.
int getchar (void) ------ 从标准输入(stdin)读入一个字符,直到遇到换行符或者结束符(EOF),返回字符对应的ASCII值,由宏实现:#define getchar() getc(stdin).
运行结果:


问题1:
当程序运行到18行输入一个 + 再按Enter之后, 再运行到23行提示输入第二个数之后,窗口并没有等待输入,直接运行到42行打印结果.
也就是说 getchar()之后没有等待输入.
解释:
对于 getchar(),如果是用Enter结束输入时,Enter 键值也会被存入标准输入缓冲中,当此程序23行执行时直接读到Enter,结束,所以才会出现不等待的现象.
解法1:
既然是因为标准输入缓冲中还有残留的数据,那我用fflush(stdin)清空标准输入缓冲区就是了,于是修改上面的代码如下:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int main(int argc, char* argv[]) 6 { 7 double firstNum, secondNum; 8 double result = 0; 9 int operatoring; 10 11 char buff1[10]; 12 char buff2[10]; 13 14 printf("please input the first number:\n"); 15 firstNum = atof(gets(buff1)); 16 17 printf("please input the operator: +, -, *, /\n"); 18 operatoring = getchar(); 19 fflush(stdin); 20 21 printf("please input the second number:\n"); 22 secondNum = atof(gets(buff2)); 23 24 switch (operatoring) 25 { 26 case 0x2B : 27 result = firstNum + secondNum; 28 break; 29 case 0x2D : 30 result = firstNum - secondNum; 31 break; 32 case 0x2A : 33 result = firstNum * secondNum; 34 break; 35 case 0x2F : 36 result = firstNum / secondNum; 37 break; 38 } 39 printf("result = %f\n", result); 40 41 return 0; 42 }
还是用Enter键结束getchar()的输入.
此代码在Windows平台(Code::Block 12.11)下运行正常,但是在Ubuntu14.04(gcc 4.8.2)下问题仍然存在,只能说fflush(stdin)在gcc下没有生效了.
PS. 关于 fflush(stdin)
C99 标准规定fflush(stdin)操作是未定义的,也就是说不一定能实现刷新功能,有些编译器作为一个扩展功能可能实现了,即依赖于具体实现,最好不要使用,移植性也不好.
也可参考C++定义 http://www.cplusplus.com/reference/cstdio/fflush/?kw=fflush 描述,原型为 int fflush(FILE *stream); 如果stream 已经被打开并指向输出流,或者是更新流(且最后一次操作是输出操作),则输出buffer中的任何待写数据将被写入到文件中,
In all other cases, the behavior depends on the specific library implementation. In some implementations, flushing a stream open for reading causes its input buffer to be cleared (but this is not portable expected behavior).
解法2:
使用 while((operatoring = getchar()) != '\n' && operatoring != EOF) 读完标准输入中的所有数据,于是代码如下:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int main(int argc, char* argv[]) 6 { 7 double firstNum, secondNum; 8 double result = 0; 9 int operatoring; 10 11 char buff1[10]; 12 char buff2[10]; 13 14 printf("please input the first number:\n"); 15 firstNum = atof(gets(buff1)); 16 17 printf("please input the operator: +, -, *, /\n"); 18 operatoring = getchar(); 19 while((operatoring = getchar()) != '\n' && operatoring != EOF); 20 printf("the last input = %d\n", operatoring); 21 22 printf("please input the second number:\n"); 23 secondNum = atof(gets(buff2)); 24 25 switch (operatoring) 26 { 27 case 0x2B : 28 result = firstNum + secondNum; 29 break; 30 case 0x2D : 31 result = firstNum - secondNum; 32 break; 33 case 0x2A : 34 result = firstNum * secondNum; 35 break; 36 case 0x2F : 37 result = firstNum / secondNum; 38 break; 39 } 40 printf("result = %f\n", result); 41 42 return 0; 43 }
代码20行打印出getchar()返回值 = 10(下面第二张图),正好是换行符\n对应的ASCII值,验证了Enter键值确实也被读入了输入缓冲区.
但是实际的功能没有实现,运行结果如下:(还是用Enter键结束getchar()的输入)


所以正确是代码如下:( 注:此操作应该置于引入不必要的缓存getchar()之后,当前输入操作secondNum = atof(gets(buff2))之前 ).
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 void Stdin_Clean(void) 6 { 7 int c; 8 do 9 { 10 c = getchar(); 11 } while (c != '\n' && c != EOF); 12 } 13 14 int main(int argc, char* argv[]) 15 { 16 double firstNum, secondNum; 17 double result = 0; 18 int operatoring; 19 20 char buff1[10]; 21 char buff2[10]; 22 23 printf("please input the first number:\n"); 24 firstNum = atof(gets(buff1)); 25 26 printf("please input the operator: +, -, *, /\n"); 27 operatoring = getchar(); 28 printf("the last input = %d\n", operatoring); 29 30 printf("please input the second number:\n"); 31 Stdin_Clean(); 32 secondNum = atof(gets(buff2)); 33 34 switch (operatoring) 35 { 36 case 0x2B : 37 result = firstNum + secondNum; 38 break; 39 case 0x2D : 40 result = firstNum - secondNum; 41 break; 42 case 0x2A : 43 result = firstNum * secondNum; 44 break; 45 case 0x2F : 46 result = firstNum / secondNum; 47 break; 48 } 49 printf("result = %f\n", result); 50 51 return 0; 52 }
运行结果:

解法3:
既然Enter键会被读入输入缓冲区,那也可以使用文件结束符(EOF)来结束getchar()的输入了,代码为:
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int main(int argc, char* argv[]) 6 { 7 double firstNum, secondNum; 8 double result = 0; 9 int operatoring; 10 11 char buff1[10]; 12 char buff2[10]; 13 14 printf("please input the first number:\n"); 15 firstNum = atof(gets(buff1)); 16 17 printf("please input the operator: +, -, *, /\n"); 18 operatoring = getchar();// use Ctrl + D instead of Enter to end the input 19 20 printf("please input the second number:\n"); 21 secondNum = atof(gets(buff2)); 22 23 switch (operatoring) 24 { 25 case 0x2B : 26 result = firstNum + secondNum; 27 break; 28 case 0x2D : 29 result = firstNum - secondNum; 30 break; 31 case 0x2A : 32 result = firstNum * secondNum; 33 break; 34 case 0x2F : 35 result = firstNum / secondNum; 36 break; 37 38 } 39 40 printf("result = %f\n", result); 41 42 return 0; 43 }
当输入运算符'+'之后直接Ctrl+D结束输入,运行结果如下:

对于EOF的使用可参考 http://www.ahlinux.com/c/22765.html.
多数人认为文件中有一个EOF,用于表示文件的结尾. 但这个观点实际上是错误的,在文件所包含的数据中,并没有什么文件结束符,对getc 而言,如果不能从文件中读取,则返回一个整数-1,这就是所谓的EOF. 返回 EOF 无非是出现了两种情况,一是文件已经读完;二是文件读取出错,反正是读不下去了. 文件结束符EOF,Windows下为组合键 Ctrl+Z,Unix/Linux下为组合键Ctrl+D.
1.EOF作为文件结束符时的情况:
EOF虽然是文件结束符,但并不是在任何情况下输入Ctrl+D(Windows下Ctrl+Z)都能够实现文件结束的功能,只有在下列的条件下,才作为文件结束符.
(1)遇到getcahr函数执行时,输入第一个字符时就直接输入Ctrl+D,就可以跳出getchar(),去执行程序的其他部分;
(2)在前面输入的字符为换行符时,接着输入Ctrl+D;
(3)在前面有字符输入且不为换行符时,要连着输入两次Ctrl+D,这时第二次输入的Ctrl+D起到文件结束符的功能,第一次的Ctrl+D使getchar开始读取键盘缓冲区中的数据。
其实,这三种情况都可以总结为只有在getchar()提示新的一次输入时,直接输入Ctrl+D才相当于文件结束符.
2.EOF作为行结束符时的情况,这时候输入Ctrl+D并不能结束getchar(),而只能引发getchar()提示下一轮的输入.
这种情况主要是在进行getchar()新的一行输入时,当输入了若干字符(不能包含换行符)之后,直接输入Ctrl+D,此时的Ctrl+D 并不是文件结束符,而只是相当于换行符的功能,即结束当前的输入.
最后,代码中使用 gets(buff1) 从标准输入中读取字符串是不安全的,没有对buff[]的宽度检查而很容易导致越界访问(gcc 编译时也会提示 warning),所以最好是用fgets(buff1, 10, stdin),第二个参数10表示最大读取10个字符到buff1中,包括换行符. 确切地说,调用fgets函数时,最多只能读入n-1个字符. 读入结束后,系统将自动在buff1最后加'\0'. 参考 http://www.cplusplus.com/reference/cstdio/fgets/.
最终的代码:
1. 使用 Ctrl+D 结束输入
1 #include <stdio.h> 2 #include <string.h> 3 #include <stdlib.h> 4 5 int main(int argc, char* argv[]) 6 { 7 double firstNum, secondNum; 8 double result = 0; 9 int operatoring; 10 11 char buff1[10]; 12 char buff2[10]; 13 14 printf("please input the first number:\n"); 15 firstNum = atof(fgets(buff1, 10, stdin)); 16 17 printf("please input the operator: +, -, *, /\n"); 18 operatoring = getchar();// use Ctrl + D instead of Enter to end the input 19 20 printf("please input the second number:\n"); 21 secondNum = atof(fgets(buff2, 10, stdin)); 22 23 switch (operatoring) 24 { 25 case 0x2B : 26 result = firstNum + secondNum; 27 break; 28 case 0x2D : 29 result = firstNum - secondNum; 30 break; 31 case 0x2A : 32 result = firstNum * secondNum; 33 break; 34 case 0x2F : 35 result = firstNum / secondNum; 36 break; 37 38 } 39 40 printf("result = %f\n", result); 41 42 return 0; 43 }
2. 使用 Enter 结束输入
#include <stdio.h> #include <string.h> #include <stdlib.h> void Stdin_Clean(void) { int c; do { c = getchar(); } while (c != '\n' && c != EOF); } int main(int argc, char* argv[]) { double firstNum, secondNum; double result = 0; int operatoring; char buff1[10]; char buff2[10]; printf("please input the first number:\n"); firstNum = atof(fgets(buff1, 10, stdin)); printf("please input the operator: +, -, *, /\n"); operatoring = getchar(); printf("please input the second number:\n"); Stdin_Clean(); secondNum = atof(fgets(buff2, 10, stdin)); switch (operatoring) { case 0x2B : result = firstNum + secondNum; break; case 0x2D : result = firstNum - secondNum; break; case 0x2A : result = firstNum * secondNum; break; case 0x2F : result = firstNum / secondNum; break; } printf("result = %f\n", result); return 0; }
浙公网安备 33010602011771号