C语言 getchar()原理及易错点解析

C语言 getchar()原理及易错点解析文章目录一.getchar()系列1.getchar()工作原理及作用2.使用getchar()清理回车\n3.使用getchar()清理缓存一.getchar()系列1.getchar()工作原理及作用工作原理:getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了,第一次getchar()时,…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

一.getchar()系列

1.getchar()工作原理及作用

  • 工作原理:getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了,第一次getchar()时,确实需要人工的输入,但是如果你输了多个字符,以后的getchar()再执行时就会直接从缓冲区中读取了。
    实际上是 输入设备->内存缓冲区->getchar()
    你按的键是放进缓冲区了,然后供程序getchar()
    你有没有试过按住很多键然后等一会儿会滴滴滴滴响,就是缓冲区满了,你后头按的键没有存进缓冲区.
    键盘输入的字符都存到缓冲区内,一旦键入回车,getchar就进入缓冲区读取字符,一次只返回第一个字符作为getchar函数的值,如果有循环或足够多的getchar语句,就会依次读出缓冲区内的所有字符直到’\n’.要理解这一点,之所以你输入的一系列字符被依次读出来,是因为循环的作用使得反复利用getchar在缓冲区里读取字符,而不是getchar可以读取多个字符,事实上getchar每次只能读取一个字符.如果需要取消’\n’的影响,可以用getchar();来清除,这里getchar();只是取得了’\n’但是并没有赋给任何字符变量,所以不会有影响,相当于清除了这个字符.

  • 作用1:从缓冲区读走一个字符,相当于清除缓冲区。

  • 作用2:前面的scanf()在读取输入时会在缓冲区中留下一个字符’\n’(输入完按回车键所致),所以如果不在此加一个getchar()把这个回车符取走的话,接下来的scanf()就不会等待从键盘键入字符,而是会直接取走这个“无用的”回车符,从而导致读取有误。

2.使用getchar()清理回车\n

这个问题转载自n_s_X14,但是作者在文章最后留了一个问题,现在在这里给大家解释一下原因。

文章的源码为:

#include <stdio.h>

int main(void){ 
   
    char m[40];
    char n;
    printf("please input first str:\n");    //提示用户输入第一个字符串
    scanf("%s",m);                         //获取用户第一个输入字符串
    printf("you input str is :%s\n",m);    //输出用户的输入的第一个字符串
    printf("input second char :\n");        //提示用户输入第二个字符
    scanf("%c",&n);                         //获取用户的第二个字符
    printf("now you input second char is :%c\n",n);//输出用户输入的第二个字符
    return 0;
    
}

Output:

please input first str:
abc
you input str is :abc
input second char :
now you input second char is :

Program ended with exit code: 0

问题:我们第一次输入abc后成功打印出来了you input str is :abc,但是执行到printf("input second char :\n");时,还没等到第二次输入就打印出来了。这是为什么??

原因:

其实在我们第一次输入并按下回车的时候,控制台一共获得了四个字符,分别是:a、b、c、回车(enter)。但是因为scanf()方法遇到非字符的时候会结束从控制台的获取,所以在输入’abc’后,按下 ‘回车(enter)’ 的同时,将’abc’这个值以字符串的形式赋值给了类型为 ‘char’ 的 ‘m’ 数组,将使用过后的字符串: ‘回车(enter)’ 保存在控制台输入的缓冲区,然后继续执行下一段输出代码,然后又要求用户输入。此时,因为上一次被使用过后的字符串被保存在缓冲区,现在scanf()方法从控制台的缓冲区获取上一次被使用过后的字符串,并只截取第一个字符: ‘回车(enter)’ ,此时控制台缓冲区才算使用完了。所以在看似被跳过的输入,其实已经scanf()方法已经获取了我们的输入了,这个输入就是一个 ‘回车(enter)’ 。

解决问题:
使用getchar()方法,清除掉abc后面的缓存(回车enter)。

#include <stdio.h>

int main(void){ 
   
    char m[40];
    char n;
    printf("please input first str:\n");    //提示用户输入第一个字符串
    scanf("%s",m);                         //获取用户第一个输入字符串
    printf("you input str is :%s\n",m);    //输出用户的输入的第一个字符串
    getchar();
    printf("input second char :\n");        //提示用户输入第二个字符
    scanf("%c",&n);                         //获取用户的第二个字符
    printf("now you input second char is :%c\n",n);//输出用户输入的第二个字符
    return 0;
    
}

Output:

please input first str:
abc
you input str is :abc
input second char :
de
now you input second char is :d
Program ended with exit code: 0

3.使用getchar()清理缓存

文章结束时留了一个问题:如果在第一次输入ab后加一个空格再回车,又会出现原来的问题,即程序只输出了ab后就自动跳过下一次的输入之间退出了,控制台输出如下图所示。
在这里插入图片描述
原因

  1. 在获取用户第一个输入字符串时,scanf("%s",&m);,我们用%s作为转换说明,%s的作用是“把输入解释成字符串。从第一个非空白字符开始,到下一个空白字符之前的所有字符都是输入。”所以scanf把输入的ab空格+回车就理解为ab+回车(ab后面没有空格),但是依然以ab空格+回车的形式存储在缓存区
  2. 我们输入ab空格+回车,在缓存区是这样存放的:在这里插入图片描述
    其中,第三格存放的为空格键。
    当程序运行完 getchar();后,只清除了第三格中的空格键,因为一次执行getchar();只清除一个缓存,留下了第四格中的回车键,因此再次出现了同样的问题。

解决问题:那么就是说只要运行两次getchar();,清除掉第三格和第四格就可以正常了。

#include <stdio.h>

int main(void){ 
   
    char m[40];
    char n;
    printf("please input first str:\n");    //提示用户输入第一个字符串
    scanf("%s",m);                         //获取用户第一个输入字符串
    printf("you input str is :%s\n",m);    //输出用户的输入的第一个字符串
    
    getchar();                              //第一次清除缓存
    getchar(); 								//第二次清除缓存
    
    printf("input second char :\n");        //提示用户输入第二个字符
    scanf("%c",&n);                         //获取用户的第二个字符
    printf("now you input second char is :%c\n",n);//输出用户输入的第二个字符
    return 0;
    
}

Output:
在这里插入图片描述
由此可见,当我们第一次输入ab空格+回车后,程序正常运行。

进一步:如果我们输入的是a空格b+回车scanf("%s",m); 这一步只能读取到a,因为a后面有空格。但是a空格b+回车在缓冲区这样存放:
在这里插入图片描述
因此,如果想要程序正常运行则需要在输出用户的输入的第一个字符串后加入三次getchar();操作,即删除掉第二,第三,第四格的内容。

问题:如果我们输入a空格bbbbbbbb+回车,那可能需要无数个getchar();来清除缓存,这时应该怎么办??

解决方法:加入while循环while(getchar()!='\n') continue;

#include <stdio.h>

int main(void){ 
   
    char m[40];
    char n;
    printf("please input first str:\n");    //提示用户输入第一个字符串
    scanf("%s",m);                         //获取用户第一个输入字符串
    printf("you input str is :%s\n",m);    //输出用户的输入的第一个字符串
    
    while(getchar()!='\n')					//通过while循环删除缓存
        continue;
        
    printf("input second char :\n");        //提示用户输入第二个字符
    scanf("%c",&n);                         //获取用户的第二个字符
    printf("now you input second char is :%c\n",n);//输出用户输入的第二个字符
    return 0;
    
}

这时,我们输入a空格bbbbbbbb+回车,程序正常运行。
在这里插入图片描述
解析:

 while(getchar()!='\n')
        continue;

可以看出这段代码代替了无数个getchar(),他的作用是跳过剩余的输入行
在这里插入图片描述
第一次while循环消除第二格缓存,第二次while循环消除第三格缓存……直到第八次。最后一次同样,getchar()也消除了回车。

while(getchar()!='\n')可以拆分成两步,

  • 第一步调用getchar()方法(这里getchar();只是取得了’\n’但是并没有赋给任何字符变量,所以不会有影响,相当于清除了这个字符)。
  • 第二步判断获取到的缓存是否等于’\n’。

4.混合scanf()与getchar()

假设程序要求用getchar()处理字符输入,用scanf()处理数值输入,这两个函数都能很好的完成任务,但是不能混合使用因为getchar()读取每个字符,包括空格、制表符和换行符;而scanf()在读取数字时则会跳过空格、制表符和换行符。

例:
要求用户输入一个字母和两个数字,输出以第一个数字为行数,第二个数字为列数,以字母为内容的数列,要求可以不断输入直至键入回车退出程序:

#include <stdio.h>
void display(char cr,int lines,int width);
int main(int argc, const char * argv[]) { 
   
   
    int ch;
    int rows,cols;
    printf("Enter a character and two integers:\n");
    while((ch=getchar())!= '\n'){ 
   
        scanf("%d %d", &rows,&cols);
        display(ch, rows, cols);
        printf("Enter another character and two integers;\n");
        printf("Enter a newline to quit.\n");
    }
    printf("Bye.\n");
    return 0;
    
    }
void display(char cr,int lines,int width){ 
   
    int row,col;
    
    for(row=1; row<= lines; row++){ 
   
        for(col =1; col<=width; col++){ 
   
            putchar(cr);
        }
        putchar('\n');
            }
}

output:
在这里插入图片描述
我们发现,在第一次输入成功打印后,程序自动退出。这明显不符合我们的题目要求。
原因是,输入的c23其实是c23+换行符,scanf()函数把这个换行符留在了缓存中。getchar()不会跳过换行符,所以在进入下一轮迭代时,还没来得及输入字符,它就读取了换行符,然后将其赋值给了ch。而ch是换行符正式终止循环的条件。

如何改进??

  1. 我们需要删除scanf()函数留在缓存中的换行符即可。
  2. 在if语句中使用一个break语句,可以在scanf()的返回值不等于2时终止程序,即如果一个或两个输入值不是整数或者遇到文件结尾就终止程序。
#include <stdio.h>
void display(char cr,int lines,int width);
int main(int argc, const char * argv[]) { 
   
   
    int ch;
    int rows,cols;
    printf("Enter a character and two integers:\n");
    while((ch=getchar())!= '\n'){ 
   
        if( scanf("%d %d", &rows,&cols)!=2 ){ 
   
            break;
        }
        display(ch, rows, cols);
        
        while(getchar()!='\n'){ 
   
            continue;
        }
        printf("Enter another character and two integers;\n");
        printf("Enter a newline to quit.\n");
    }
    printf("Bye.\n");
    return 0;
    
    }
void display(char cr,int lines,int width){ 
   
    int row,col;
    
    for(row=1; row<= lines; row++){ 
   
        for(col =1; col<=width; col++){ 
   
            putchar(cr);
        }
        putchar('\n');
            }
}

Output:
在这里插入图片描述


题外话:

scanf()中转化符的问题
在这里插入图片描述
在这里插入图片描述
问题:从上面两张图片中可以看出,当scanf("%d",&c);改为scanf("%c",&c);时,控制台中出现了图二的问题。character为什么为空白??

原因:
如果格式是%c,那么任何字符都是它想要的,所以第二个程序中的第二个scanf(“%c”)会得到‘+’后面的空格’ ‘。如果格式是%d,则会忽略任何空白字符(空格、回车、制表符等),忽略的意思是,从缓冲区里删除,但并不保存;如果遇到数字,则拿出并保存给后面的整数,也就是说%d的时候,scanf想要的字符是数字和空白符。所以第一个程序里的第二个scanf(“%d”)忽略掉了空格,正确输入了数字。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-用户IM,转载请注明出处:https://javaforall.cn/180329.html原文链接:https://javaforall.cn

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • 关于 Sensor flicker/banding现象的解释「建议收藏」

    关于 Sensor flicker/banding现象的解释「建议收藏」目录1、基本概念2、30fps,1帧内banding过程演示3、30fps,帧与帧之间banding过程演示4、25fps,帧与帧之间banding过程演示5、总结flicker交流电网中的传输的能量并不是稳定不变的,而是随着一个固定频率变化的,这个频率一般被称为工频,例如中国是50Hz,美国是60Hz。工频由电力系统决定。工频的带来的这种能量变化称为flicker。下面以50HZ为例进行解释,交流电以1/50s,即20ms的周期进行变化,其变化规律如图所示:而对于能量来说,并没有正负之分,因此能量

    2022年10月13日
  • acwing-361. 观光奶牛(0/1分数规划)「建议收藏」

    acwing-361. 观光奶牛(0/1分数规划)「建议收藏」给定一张 L 个点、P 条边的有向图,每个点都有一个权值 f[i],每条边都有一个权值 t[i]。求图中的一个环,使“环上各点的权值之和”除以“环上各边的权值之和”最大。输出这个最大值。注意:数据保证至少存在一个环。输入格式第一行包含两个整数 L 和 P。接下来 L 行每行一个整数,表示 f[i]。再接下来 P 行,每行三个整数 a,b,t[i],表示点 a 和 b 之间存在一条边,边的权值为 t[i]。输出格式输出一个数表示结果,保留两位小数。数据范围2≤L≤1000,2≤P≤50

  • emwin仪表控件_仪表开发工程师

    emwin仪表控件_仪表开发工程师本文导读i.MXRT1052具有增强型显示接口(eLCDIF),支持8/16/18/24位的数据总线模式。M1052核心板最高可支持1366×768的高清图像显示,芯片内置音频接口,搭载免费的emWin图形库可广泛应用多媒体设计。一、emWin介绍emWin是Segger公司针对嵌入式平台开发的稳定、高效的图形软件库,适合用于任何图形LCD的操作应用,并可输出高质量的无锯齿的文字和图形,通过调用…

    2022年10月14日
  • 8种垂直居中的方法

    八种垂直居中的方法垂直居中的需求经常遇到,通过资料实践了八种垂直居中的方法。以下的方法都围绕着该HTML展开HTML代码<divstyle=”width:300px;height:300px;”class=”wrap”><divstyle=”width:100px;height:100px”class=”box”></div></div>CSS方法1(常用):display:flex.wra

  • 动感地带亲情省

    动感地带亲情省注:本文转自网络为进一步满足客户国内长途及国内漫游通话需求,中国移动北京公司将于近期推出长漫亲情省业务,现就相关事宜通知如下:  一、推出时间  2009年4月18日0时  二、目标客户动感地带、

  • glassfish server_glassware

    glassfish server_glassware介绍以后再补,先给出怎么用.官网:https://glassfish.java.net下载地址https://glassfish.java.net/download.html解压到你的工作目录下,先看jdk是否安装了,java-version.启动glassfish,进入到你的glassfish目录下,运行./asadminstart-domain接

发表回复

您的电子邮箱地址不会被公开。

关注全栈程序员社区公众号