C语言中的输入与scanf读取的原理详解

在做PTA时,经常会碰到多行输入或混合数据类型的情况。此时若对scanf没有很好的理解,那么就会不可避免地产生一些隐秘的bug,从而致使“部分正确”甚至“答案错误”,消磨心神。

这篇文章主要从输入与scanf读取两个角度,对输入数据的分类、格式化输入、终端输入与缓冲区以及scanf的原理做了详细分析与理解,供大家参考。

注:以下由外界输入的形式一律按照“...-...-...”

一、输入

        1、输入数据的分类

                1.1 数

数主要分为整型和浮点型,包括数字

(这篇文章里认为数字即0~9)

                1.2 字符

字符分为空白字符与非空白字符。空白字符包括空格、制表、回车

(严格来讲在windows系统下实际是换行,但这篇文章方便起见均以回车表述)

                1.3 数字

即0~9的十个数字,既可以是数,又可以是字符

(具体怎么区分,依据格式化输入的类型判定。有时候忽略了这一点会导致%c读取数字)

例如下面这个例子

char c;
scanf("%c",&c);

输入“123”,则a会被赋值为字符1

        2、格式化输入

                2.1 用户按指定格式从终端上用键盘输入数据到指定的变量中

                      此时就可以分为合法情况与非法情况

合法情况:用户输入的数据满足程序需求的格式,输入与需求对应全都匹配

非法情况:用户输入的数据不满足程序需求的格式,输入与需求存在不匹配

int n1,n2;
char c1,c2;
scanf("%d%c%d%c",&n1,&c1,&n2,&c2);

  输入“1-a-2-b-回车”,则为合法;输入“1-2-a-b-回车”,则为非法

                2.2 当数据满足格式化输入的形式时,以回车作为输入的结束

此处的“格式化输入的形式”指的是程序需求输入多少的数据,更接近“个数”的概念。需要与合法情况区分开来

切记,结束输入的这个回车会被留在缓冲区内,既作为这次输入的最后一个字符,同时也是下次输入的第一个字符

例如上述的“1-a-2-b-回车”,这个回车会被留在缓冲区。

        3、终端输入与缓冲区

                3.1 缓冲区数据不足时,终端请求输入

终端输入的数据会被缓存在缓冲区中,直到终端结束输入后再由程序读取

缓冲区存在的原因是便于输入时数据的增删修改

                3.2 缓冲区数据足够时,终端结束输入

缓冲区所剩的数据若满足格式化输入的形式,那么终端不会再请求输入。这种情况并非是终端结束输入,而是终端不请求输入。有时多行输入数据会遇到终端不请求输入的情况。

int n;
char c;
scanf("%d%c",&n,&c);
scanf("%d%c",&n,&c);

输入“1-a-2-b-3-c-回车”,第一个scanf因为缓冲区没有数据,所以终端会请求输入;第二个scanf因为缓冲区的数据足够,所以终端不会请求输入。

二、scanf读取

        1、合法情况下

                scanf对不同的数据类型有不同的特性

        对于字符型%c:scanf会读取第一个空白字符

                实质就是无论第一个是什么字符scanf均会读取

                注意,这里的“第一个”是相对于scanf中%c的位置而言

        对于整型%d或浮点型%f:scanf会读取第一个非空白字符

                而在第一个非空白字符前的所有空白字符均不会被读取

                注意,这里的“第一个”是相对于scanf中%d或%f的位置而言

int n;
char c;
scanf("%d%c",%n,&c);

输入“空格(无论多少个)/回车(无论多少个)/制表(无论多少个)-123-空格/制表/非空白字符-回车”

则n被赋值为123,c被赋值为空格/制表/非空白字符

输入“空格(无论多少个)/回车(无论多少个)/制表(无论多少个)-123-回车”

则n会被赋值为123,c会被赋值为\n。这一点,尤其难发现。

int n;
char c;
scanf("%c%d",%c,&n);    //注意上次的代码与这次的有差异

输入“空格/回车/制表/非空白字符-空格(无论多少个)/回车(无论多少个)/制表(无论多少个)-123-回车”

则c被赋值为空格/回车/制表/非空白字符,n被赋值为123

        2、非法情况下

                scanf会从第一个类型不匹配的数据开始停止此次的读取

                直到输入数据满足格式化输入的形式后执行下一行代码

此时,从第一个类型不匹配的数据开始(包括该数据),直到输入的数据足够后,这期间输入的数据会被缓存在缓冲区。之前的数据则被清空,或者说不予理会。

int n1,n2;
char c1,c2;
scanf("%d%c%d%c",&n1,&c1,&n2,&c2);

输入“1-a-b-2-回车”,则n1被赋值为1,c1被赋值为a,n2与c2不被赋值

此时,“b-2-回车”被缓存到缓冲区,等待下一次的读取

                下一个scanf则会从缓冲区的第一个数据开始读取

此时,你仍有可能会在终端上被要求输入数据(这取决于缓冲区中的数据是否足够)。可是要明白此时真正被scanf正在读取的并非你输入的数据,而是缓冲区中的数据。但是你输入的数据仍然要满足格式化输入的形式才能结束输入,此时你输入的数据与缓冲区已有的数据一并被scanf读取,若合法则结束读取;若不合法则继续上述非法情况

int n1,n2;
char c1,c2;
scanf("%d%c%d%c",&n1,&c1,&n2,&c2);
scanf("%c%d%d%c",&c1,&n1,&n2,&c2);    //注意上一行与此行代码有差异

情况一,缓冲区数据不足: 

        输入“1-a-b-2-回车”,则n1被赋值为1,c1被赋值为a,n2与c2不被赋值

        此时,“b-2-回车”被缓存到缓冲区,等待下一次的读取

        执行到下一行代码,scanf需要读取,然而此时缓冲区的数据不满足格式化输入的形式,因此终端请求输入

        输入“3-c-4-d-回车”,但真正被scanf读取的数据是“b-2-回车-3-c-4-d-回车”

        则c1被赋值为b,n1被赋值为2,n2被赋值为3,c2被赋值为c

情况二,缓冲区数据足够:

        输入“a-1-2-b-回车”,则n1、c1、n2、c2均不被赋值

        此时,“a-1-2-b-回车”被缓存在到缓冲区,等待下一次的读取

        执行到下一行代码,scanf需读取,然而此时缓冲区的数据满足格式化输入的形式,因此终端不请求输入

        则c1被赋值为a,n1被赋值为12,n2与c2不被赋值,缓冲区剩下“b-回车”

总结:

只要对这篇文章所提到的每一点都有深刻真切的领会,并能牢记,在分析自己写的代码时运用自如,那么对于scanf就不会有更多的疑惑。共勉。

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值