zoukankan      html  css  js  c++  java
  • scanf函数

    scanf函数

    与printf函数一样,都被定义在头文件stdio.h里,因此在使用scanf函数时要加上#include <stdio.h>。它是格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中。

    函数原型:   int scanf(const char *format,...);

    函数 scanf() 是从标准输入流stdio (标准输入设备,一般是键盘)中读内容的通用子程序,
    可以根据说明的格式读入若干个数据,并保存在对应地址的变量中。
    其调用形式为: scanf("<格式说明字符串>",<变量地址列表>);变量地址要求有效,并且与格式说明的次序一致。

    scanf()函数返回成功赋值的数据项数,读到文件末尾出错时则返回EOF。

    例如 scanf("%d %d",&a,&b);

    如果a和b都被成功读入,那么scanf的返回值就是2
    如果只有a被成功读入,返回值为1
    如果a和b都未被成功读入,返回值为0
    如果遇到错误或遇到end of file,返回值为EOF。
    且返回值为int型.

    例:使用scanf函数输入数据。

    #include<stdio.h>
    int main(void)
    {
        int a,b,c;
        printf("输入a,b,c
    ");
        scanf("%d%d%d",&a,&b,&c);
        printf("a=%d,b=%d,c=%d
    ",a,b,c);
        fflush(stdin);
        return 0;
    }

    &a,&b,&c中的&是地址运算符,&a指a在内存中的地址。scanf的作用是:按照a,b,c的内存地址将输入的数据存到a,b,c中去。变量a,b,c的地址是在编译连续阶段分配的(存储顺序由编译器决定)。
    这里注意:如果scanf中%d是连着写的如“%d%d%d”,在输入数据时,数据之间不可以加逗号,只能是空格或tab键或者回车键——“2 3 4” 或 “2(按tab)3(按tab)4(按tab)”。若是“%d,%d,%d”,则在输入数据时需要加“,”,如“2,3,4”.

    问题一

    如何让scanf()函数正确接受有空格的字符串?如: I love you!
    1 #include<stdio.h>
    2 int main(void)
    3 {
    4     char str[80];
    5     scanf("%s",str);
    6     printf("%s",str);
    7     return 0;
    8 }

    输入:I love you!

    上述程序并不能达到预期目的,scanf()扫描到"I"后面的空格就认为对str的赋值结束,并忽略后面的"love you!".这里要注意是"love you!"还在键盘缓冲区(关于这个问题,网上我所见的说法都是如此,但是,我经过调试发现,其实这时缓冲区字符串首尾指针已经相等了,也就是说缓冲区清空了,scanf()函数应该只是扫描stdin流,这个残存信息是在stdin中)。我们改动一下上面的程序来验证一下:

     1 #include<stdio.h>
     2 #include<windows.h>
     3 int main(void)
     4 {
     5     char str[80],str1[80],str2[80];
     6     scanf("%s",str);/*此处输入:Iloveyou!*/
     7     printf("%s
    ",str);
     8     Sleep(5000);/*这里等待5秒,告诉你程序运行到什么地方*/
     9     /*
    10     不是sleep(5)
    11     1,函数名是Sleep不是sleep。
    12     2,C/C++中,unsignedSleep(unsigned)应该是毫秒ms.
    13     */
    14     scanf("%s",str1);/*这两句无需你再输入,是对stdin流再扫描*/
    15     scanf("%s",str2);/*这两句无需你再输入,是对stdin流再扫描*/
    16     printf("%s
    ",str1);
    17     printf("%s
    ",str2);
    18     return 0;
    19 }

    输入:I love you!

    输出:

    I
    love
    you!
    好了,原因知道了,所以结论是:残留的信息 love you是存在于stdin流中,而不是在键盘缓冲区中。那么scanf()函数能不能完成这个任务?回答是:能!别忘了scanf()函数还有一个 %[] 格式控制符(如果对%[]不了解的请查看本文的上篇),请看下面的程序:
    1 #include<stdio.h>
    2 int main(void)
    3 {
    4     char str[50];
    5     scanf("%[^
    ]",str);/*scanf("%s",string);不能接收空格符*/
    6     printf("%s
    ",str);
    7     return 0;
    8 }

    问题二

    键盘缓冲区残余信息问题
     1 #include<stdio.h>
     2 int main(void)
     3 {
     4     int a;
     5     char c;
     6     while(c!='N')
     7     {
     8         scanf("%d",&a);
     9         scanf("%c",&c);
    10         printf("a=%dc=%c
    ",a,c);/*printf("c=%d
    ",c);*/
    11     }
    12     return 0;
    13 }

    scanf("%c", &c);这句不能正常接收字符,什么原因呢?我们用printf("c = %d ", c);将C用int表示出来,启用printf("c = %d ", c);这一句,看看scanf()函数赋给C到底是什么,结果是c=10 ,ASCII值为10是什么?换行即 .对了,我们每击打一下"Enter"键,向键盘缓冲区发去一个“回车”( ),一个“换行"( ),在这里 被scanf()函数处理掉了(姑且这么认为吧^_^),而 被scanf()函数“错误”地赋给了c.解决办法:可以在两个scanf()函数之后加getchar()。

    也可以使用fflush函数。fflush()函数冲洗流中的信息,该函数通常用于处理磁盘文件。

     1 #include<stdio.h>
     2 int main(void)
     3 {
     4     int a;
     5     char c;
     6     while(c!='N')
     7     {
     8         scanf("%d",&a);
     9         fflush(stdin);
    10         scanf("%c",&c);
    11         fflush(stdin);
    12         printf("a=%dc=%c
    ",a,c);
    13     }
    14     return 0;
    15 }

    问题三

    输入类型与格式化字符串不匹配导致stdin流的阻塞。
     1 #include<stdio.h>
     2 int main(void)
     3 {
     4     int a=0,b=0,c=0,ret=0;
     5     ret=scanf("%d%d%d",&a,&b,&c);
     6     printf("第一次读入数量:%d
    ",ret);
     7     ret=scanf("%d%d%d",&a,&b,&c);
     8     printf("第二次读入数量:%d
    ",ret);
     9     return 0;
    10 }
    我们定义了a,b,c三个变量来接受输入的内容,定义了变量ret来接收scanf函数的返回值。
    正确输入的话:

    但是当输入内容与格式换字符串不匹配时,结果会令人大跌眼镜。

    执行到第一个scanf时,当输入字符’b’的时候与 ret=scanf("%d%d%d",&a,&b,&c);中的格式化字符串不匹配,stdin流被阻塞,scanf函数不在 读取后面的部分,直接将1返回,表示只将stdin流中的1读入到了变量a中。
    执行到第二个scanf时,字符’b’还是与格式化字符串不匹配,stdin流仍然被阻塞,所以没有提示输入,scanf函数将0返回。

    将代码作如下修改,可以有力的证明上述结论。

     1 #include<stdio.h>
     2 int  main(void)
     3 {
     4     int a=0,b=0,c=0,ret=0;
     5     ret=scanf("%d%d%d",&a,&b,&c);
     6     printf("第一次读入数量:%d
    ",ret);
     7     ret=scanf("%c%d%d",&a,&b,&c);
     8     printf("第二次读入数量:%d
    ",ret);
     9     return 0;
    10 }

    当把第二个scanf函数内的格式化字符串改为”%c%d%d”时,运行结果如下

    执行到第一个scanf函数时,由于输入’b’的原因scanf函数直接返回1,stdin流阻塞。
    执行到第二个scanf函数时,字符’d’与格式化字符串”%c%d%d”中的%c匹配,stdin流终于疏通,在输入6,则将变量a,b,c分别赋值为98(‘b’的ASCII码)、2、6,scanf函数返回3。
    由上述问题可知,当使用scanf函数时,如果遇到一些匪夷所思的问题,在scanf函数后正确使用fflush(stdin);,清空输入缓冲区,可以解决很多问题。以本题为例:
     1 #include<stdio.h>
     2 int main(void)
     3 {
     4     int a=0,b=0,c=0,ret=0;
     5     ret=scanf("%d%d%d",&a,&b,&c);
     6     fflush(stdin);
     7     printf("第一次读入数量:%d
    ",ret);
     8     ret=scanf("%d%d%d",&a,&b,&c);
     9     fflush(stdin);
    10     printf("第二次读入数量:%d
    ",ret);
    11     return 0;
    12 }

    运行结果:

    问题四

    如何处理scanf()函数误输入造成程序死锁或出错
    1 #include<stdio.h>
    2 int main(void)
    3 {
    4     int a,b,c;
    5     scanf("%d,%d",&a,&b);
    6     c=a+b;/*计算a+b*/
    7     printf("%d+%d=%d",a,b,c);
    8     return 0;
    9 }

    如上程序,如果正确输入a,b的值,那么没什么问题,但是,你不能保证使用者每一次都能正确输入,一旦输入了错误的类型,你的程序不是死锁,就是得到一个 错误的结果,呵呵,这可能所有人都遇到过的问题吧?解决方法:scanf()函数执行成功时的返回值是成功读取的变量数,也就是说,你这个scanf() 函数有几个变量,如果scanf()函数全部正常读取,它就返回几。但这里还要注意另一个问题,如果输入了非法数据,键盘缓冲区就可能还个有残余信息问题。正确的例程:

     1 #include<stdio.h>
     2 int main(void)
     3 {
     4     int a,b,c;
     5     while(scanf("%d%d",&a,&b)!=2)
     6     fflush(stdin);
     7     c=a+b;
     8     printf("%d+%d=%d",a,b,c);
     9     return 0;
    10 }
    补充
    fflush(stdin)这个方法在GCC下不可用。(在VC6.0下可以)
    以下是 C99 对 fflush 函数的定义:
    int fflush(FILE *stream);
    如果stream指向输出流或者更新流(update stream),并且这个更新流
    执行的操作不是输入,那么fflush函数将把任何未被写入的数据写入stream
    指向的文件(如标准输出文件stdout)。否则,fflush函数的行为是不确定的。
    C和C++的标准里从来没有定义过 fflush(stdin)。
    fflush(NULL)清空所有输出流和上面提到的更新流。如果发生写错误,fflush
    函数会给那些流打上错误标记,并且返回EOF,否则返回0。
    由此可知,如果 stream 指向输入流(如 stdin),那么 fflush 函数的行为是不确定的。故而使用
    fflush(stdin) 是不正确的,至少是移植性不好的。
    可采用如下方法:
    方法一:
     1 /*此函数可以和scanf函数一起使用,但使用%c输入时要注意,即此函数只能用于缓冲区非空的情况*/
     2 #include<stdio.h>
     3 void flush()
     4 {
     5     charc;
     6     while((c=getchar())!='
    '&&c!=EOF);
     7 }
     8 intmain(void)
     9 {
    10     int a,b,c;/*计算a+b*/
    11     while(scanf("%d%d",&a,&b)!=2)
    12     flush();
    13     c=a+b;
    14     printf("%d+%d=%d",a,b,c);
    15     return 0;
    16 }
    方法二:
    使用getchar()代替fflush(stdin)

    程序示例:

     1 #include<stdio.h>
     2 int main(void)
     3 {
     4     int i,c;
     5     while(1)
     6     {
     7         printf("Pleaseinputaninteger:");
     8         scanf("%d",&i);
     9         if(feof(stdin)||ferror(stdin))
    10         {
    11             //如果用户输入文件结束标志(或文件已被读完),或者发生读写错误,则退出循环
    12             //dosomething
    13             break;
    14         }
    15         //没有发生错误,清空输入流。通过while循环把输入流中的余留数据“吃”掉
    16         while((c=getchar())!='
    '&&c!=EOF);
    17         //可直接将这句代码当成fflush(stdin)的替代,直接运行可清除输入缓存流
    18         //使用scanf("%*[^
    ]");也可以清空输入流,不过会残留
    字符。
    19         printf("%d
    ",i);
    20     }
    21     return 0;
    22 }
  • 相关阅读:
    微信公众号开发:3、自定义菜单
    微信公众号开发:2、消息处理
    微信公众号开发:1、服务器配置
    基于.NetCore3.1系列 —— 日志记录之初识Serilog
    基于.NetCore3.1系列 —— 日志记录之日志核心要素揭秘
    基于.NetCore3.1系列 —— 日志记录之自定义日志组件
    基于.NetCore3.1系列 —— 日志记录之日志配置揭秘
    基于.NetCore3.1系列 —— 使用Swagger导出文档 (番外篇)
    基于.NetCore3.1系列 —— 使用Swagger导出文档 (补充篇)
    基于.NetCore3.1系列 —— 使用Swagger做Api文档 (下篇)
  • 原文地址:https://www.cnblogs.com/huashanqingzhu/p/5057945.html
Copyright © 2011-2022 走看看