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 }
  • 相关阅读:
    Redis源码分析(二十一)--- anet网络通信的封装
    leetcode 总结part1
    leetcode String to Integer (atoi)
    leetcode 165. Compare Version Numbers
    leetcode 189. Rotate Array
    leetcode 168. Excel Sheet Column Title
    leetcode 155. Min Stack
    leetcode 228. Summary Ranges
    leetcode 204. Count Primes
    leetcode 6. ZigZag Conversion
  • 原文地址:https://www.cnblogs.com/huashanqingzhu/p/5057945.html
Copyright © 2011-2022 走看看