一个关于使用链表增加删除人名的小程序,在使用getchar()函数,得到输入的菜单选项时,出现了问题,现记录如下:
【菜单部分代码如下:】
#include <stdio.h> int main(int argc, char * argv [ ]) { char cOpt; while(1) { print(); cOpt = getchar(); switch(cOpt) /* switch里的变量只能是整型或字符型 */ { case 'l': { list_name(); break; } case 'a': { add_name(); break; } case 'd': { del_name(); break; } case 'x': { return 0; break; } default: { break; } } } return 0; }
【错误现象:】
输入菜单选项时,多输出一次菜单。
【解决问题过程:】
1.我在每一个语句后增加的打印语句。
printf("1. test ");
发现每次都会多执行一次default语句。
2.查看cOpt输出结果
cOpt = getchar(); printf("1. cOpt = 0x%x ", cOpt); switch(cOpt) { … }
发现cOpt的值有0xa,这是换行符的ASCII码。
通过对缓冲区的研究发现以下结论:
我的输入被放入了输入缓冲区,这里的回车操作,既有确定作用又是字符,所以回车' '它也跟着进了缓冲区,这个时候getchar()会从stdin流缓冲区中读取刚才的输入,一次只读一个字符,所以字符‘l’就被拿出来了,赋值给了cOpt,然后使用switch选择菜单项执行,这是第1次显示菜单栏。字符‘l’被取出的同时,也被缓冲区释放了,而此时缓冲区还有回车(' ')。所以在while(1)第2次循环时,根本不需要输入就读出了回车(' ')字符,赋值给cOpt,在switch里判断后回车(' ')字符属于未定义的,必然要执行default分支。此时,回车字符也被缓冲区释放了,所以再次调用getchar时,程序就等着用户按键。
所以,getchar的调用就是直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才等待用户按键。
【解决方案:】
【法1:】知道原因后,我在switch前的cOpt = getchar();后增加下列语句
if (cOpt == ' ') { cOpt = getchar(); }
【法2:】
#include <stdio.h> int main(int argc, char * argv [ ]) { char cOpt; while(1) { print();
/* 解决方案2 */ do{ cOpt = getchar(); }whiel((cOpt != 'l') && (cOpt != 'a') && (cOpt != 'd') && (cOpt != 'x')) if (cOpr == 'l') { list_name(); } else if (cOpr == 'a') { ShowPrePage(); } else if (cOpr == 'd') { del_name(); } else if (cOpr == 'x') { return 0; } } return 0; }
目的是为了释放回车字符,使程序跳过对回车字符的操作。
补充:
缓冲区分为三种类型:全缓冲、行缓冲和不带缓冲。
1、全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作。全缓冲的典型代表是对磁盘文件的读写。
2、行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键换行时才进行实际的I/O操作。典型代表是键盘输入数据。
3、不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
缓冲区的刷新
缓冲区会在以下三种情况下被刷新:
1、缓冲区满
2、执行flush刷新缓冲区的语句
3、程序正常结束。