白旭的博客欢迎您

既然选择了远方,便只顾风雨兼程!

18.28 getchar()函数与缓冲区问题

  一个关于使用链表增加删除人名的小程序,在使用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\n");

  发现每次都会多执行一次default语句。

2.查看cOpt输出结果

cOpt = getchar();
printf("1. cOpt = 0x%x\n", cOpt);
switch(cOpt)
{
      …
}

 

  发现cOpt的值有0xa,这是换行符的ASCII码。

通过对缓冲区的研究发现以下结论:

  我的输入被放入了输入缓冲区,这里的回车操作,既有确定作用又是字符,所以回车'\n'它也跟着进了缓冲区,这个时候getchar()会从stdin流缓冲区中读取刚才的输入,一次只读一个字符,所以字符‘l’就被拿出来了,赋值给了cOpt,然后使用switch选择菜单项执行,这是第1次显示菜单栏。字符‘l’被取出的同时,也被缓冲区释放了,而此时缓冲区还有回车('\n'。所以在while(1)第2次循环时,根本不需要输入就读出了回车('\n'字符,赋值给cOpt,在switch里判断后回车('\n'字符属于未定义的,必然要执行default分支。此时,回车字符也被缓冲区释放了,所以再次调用getchar时,程序就等着用户按键。

  所以,getchar的调用就是直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才等待用户按键。

【解决方案:】

  【法1:】知道原因后,我在switch前的cOpt = getchar();后增加下列语句

if (cOpt == '\n')
{
    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、程序正常结束。


 

posted on 2019-07-19 11:26  小学生_白旭  阅读(394)  评论(0编辑  收藏  举报

导航