scanf常见的注意问题

c基础总结 专栏收录该内容
19 篇文章 0 订阅

scanf()函数

  • 是通用终端格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。可以读入任何固有类型的数据并自动把数值变换成适当的机内格式。
  • 其调用格式为: scanf(“<格式化字符串>”,<地址表>);
  • scanf()函数接收输入数据时,遇以下情况结束一个数据的输入:(不是结束该scanf函数, scanf函数仅在每一个数据域均有数据,并按回车后结束)。
    ① 遇空格、“回车”、“跳格”键。
    ② 遇宽度结束。
    ③ 遇非法输入。

问题一

  • 如何让scanf()函数正确接受有空格的字符串?如: I love you!
#include <stdio.h> 
int main()
{
    char arr[80];
    scanf("%s",arr);
    printf("%s",arr);   
    return 0;
}
输入:I love you
输出:I

上述程序并不能达到预期目的。因为scanf扫描到”I”后面的空格就认为对str的扫描结束(空格没有被扫描),并忽略后面的” love you”。

原因:残留的信息 love you是存在于stdin流中,而不是在键盘缓冲区中。

改进:

#include<stdio.h>
int main(void)
{
    char arr[50];
    scanf("%[^\n]",arr);//不能接收空格符
    printf("%s\n",arr);
    return 0;
}

问题二

  • 键盘缓冲区残余信息问题
#include<stdio.h>
int main(void)
{
    int a;
    char c;
    do{
        scanf("%d",&a);
        scanf("%c",&c);
        printf("a=%d c=%c\n",a,c);
    }while(c!='N');
    return 0;
}

输入:1 4
输出:a=1 c=
     a=4 c=

scanf(“%c”, &c);这句不能正常接收字符。我们每敲一下”Enter”键,向键盘缓冲区发去一个“回车”(\r),一个“换行”(\n),在这里\r被scanf()函数处理掉(执行拿数据),而\n被scanf()函数“错误”地赋给了c。

改进:

  • fflush(stdin)
  • while(ch=getchar() != ‘\n’)
    {
    ;
    }
#include<stdio.h>
int main(void)
{
    int a;
    char c;
    do{
        scanf("%d",&a);
        fflush(stdin); //刷新输入缓冲区
        scanf("%c",&c);
        fflush(stdin);
        printf("a=%d c=%c\n",a,c);
    }while(c!='N');
    return 0;
}

输入:1
     4
输出:a=1 c=4
#include<stdio.h>
int main(void)
{
    int a;
    char c;
    do{
        scanf_s("%d", &a);
        getchar();
        scanf_s("%c", &c);
        printf("a=%d c=%c\n", a, c);
    } while (c != 'N');
    return 0;
}

输入:1 4
输出:a=1 c=4
  • “空格符”来处理缓冲区残余信息的示例:

版本1:运行出错的程序

#include<stdio.h>
int main(void)
{
    int i;
    char j;
    for(i=0;i<10;++i)
    scanf("%c",&j);/*这里%前没有空格*/
    printf("%c",j);/*在输入十个字符之后*/
    return 0;
}

版本2:使用了空格控制符后

include<stdio.h>
int main(void)
{
    int i;
    char j;
    for(i=0;i<10;++i)
    scanf(" %c",&j);/*注意这里%前有个空格*/
    printf("%c",j);
    /*在输入十个字符之后, 验证打印出来的字符是否是自己
    输入的最后一个字符(即输入的第十个字符)*/
    return 0;
}
运行第一个版本(错误的程序)
我们输入:
0 1 2 3 4 5 6 7 8 9
结果是一个空字符
再运行第二个版本(正确的程序)
同样输入:
0 1 2 3 4 5 6 7 8 9
这一次就显示字符9,故此程序正确。

结论:就是%前面的空格在起作用,“scanf()的格式控制串可以使用空白字符或其它非空白字符,使用空白字符会使scanf()函数在读操作中略去输入中的零个或多个空白字符。”
所以在%前面加上了空格(空格属于空白字符,此外还有像制表符等也属于空白字符),在输入过程中,将略去输入中的一个或多个空白字符,所以我们输入的0 1 2 3 4 5 6 7 8 9这些字符中的空白字符就被略去了,字符9也就正确的打印出来了。

问题三

  • 输入类型与格式化字符串不匹配导致stdin流的阻塞
#include<stdio.h>
int main(void)
{
    int a=0,b=0,c=0,ret=0;
    ret=scanf("%d%d%d",&a,&b,&c);
    printf("第一次读入数量:%d\n",ret);
    ret=scanf("%c%d%d",&a,&b,&c);
    printf("第二次读入数量:%d\n",ret);
    return 0;
}
    输入:1 b 2
    输出:第一次读入数量:1
    输入:6
    输出:第二次读入数量:3

执行到第一个scanf函数时,由于输入’b’的原因scanf函数直接返回1,stdin流阻塞。
执行到第二个scanf函数时,字符’b’与格式化字符串”%c%d%d”中的%c匹配,stdin流终于疏通,在输入6,则将变量a,b,c分别赋值为98(‘b’的ASCII码)、2、6,scanf函数返回3。

改进:

scanf函数后正确使用fflush(stdin);,清空输入缓冲区

#include<stdio.h>
int main(void)
{
    int a=0,b=0,c=0,ret=0;
    ret=scanf("%d%d%d",&a,&b,&c);
    fflush(stdin);
    printf("第一次读入数量:%d\n",ret);
    ret=scanf("%d%d%d",&a,&b,&c);
    fflush(stdin);
    printf("第二次读入数量:%d\n",ret);
    return 0;
}

输入:1 b 2
输出:第一次读入数量:1
输入:1 3 6
输出:第二次读入数量:3

问题四

  • 如何处理scanf()函数误输入造成程序死锁或出错
#include<stdio.h>
int main(void)
{
    int a,b,c;
    scanf("%d,%d",&a,&b);
    c=a+b;
    printf("%d+%d=%d",a,b,c);
    return 0;
}

如果正确输入a,b的值,那么没什么问题,但是,你不能保证使用者每一次都能正确输入,一旦输入了错误的类型,你的程序不是死锁,就是得到一个错误的结果

改进:

scanf()函数执行成功时的返回值是成功读取的变量数,也就是说,你这个scanf()函数有几个变量,如果scanf()函数全部正常读取,它就返回几。
但这里还要注意另一个问题,如果输入了非法数据,键盘缓冲区就可能还个有残余信息问题

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

补充:

fflush(stdin)这个方法在GCC下不可用。(在VC6.0下可以)
以下是 C99 对 fflush 函数的定义:
int fflush(FILE *stream);
如果stream指向输出流或者更新流(update stream),并且这个更新流
执行的操作不是输入,那么fflush函数将把任何未被写入的数据写入stream
指向的文件(如标准输出文件stdout)。否则,fflush函数的行为不确定。

  • 5
    点赞
  • 0
    评论
  • 11
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 游动-白 设计师:白松林 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值