详解scanf输入的一些问题

详解scanf原理
昨天的C2上机中出现了一道考验scanf原理的题《后撤步》,输入格式如下:
后撤步输入格式
第一行一个数。
【详解scanf输入的一些问题】第二行是棋子的初始坐标( x 0 , y 0 ) (x_0,y_0) (x0?,y0?) 。
第三行三个数,分别是圆心坐标 p , q p,q p,q和半径 R R R 。
接下来 n n n行,每行都是一个向量 ( x i , y i ) (x_i,y_i) (xi?,yi?) 。
后撤步输入标程
scanf("%d\n(%lld,%lld)\n%lld%lld%lld\n",&n,&x,&y,&p,&q,&r);
其中\n和(输入不当都会出错,习惯了傻瓜输入的我手足无措 。疯狂百度 。
接下来分几点详解scanf部分原理 。全篇萌新友好 。
缓冲区
键盘缓冲区是在内存的一块区域,可以以为内存中存在一块槽,键盘输入的字符从这一端按顺序进入缓冲区,用户按下回车或者缓冲区满后,scanf从另一端按顺序处理缓冲区中的字符 。
如有这么一行语句:
scanf("%d%d",&a,&b);
用户为了将123赋值给a,将45赋值给b,用键盘输入
1 ,2 ,3, 空格,4, 5,回车 。
其中,当用户没有按下回车之前,scanf无动于衷,不开始从缓冲区读取数据,用户按下回车以后,scanf开始从1开始逐个处理数据 。
%d忽略前导空白符
对于“输入一行两个用空格分隔的数字”,除了上述写法外,部分同学会使用如下代码:
scanf("%d %d",&a,&b);
同学认为,题目要求我用空格分隔,我便在scanf的格式串中写入空格,符合标准 。其实大部分情况下没有必要 。格式串中的空白符容易引发奇怪的错误,会在下面解释 。
%d有个特性便是忽略前导空白符,所以使用
scanf("%d%d",&a,&b);
便可 。分析如下 。
键盘输入1 2 3 空格 4 5 \n 。scanf的格式串%d%d便开始从缓冲区吃数据 。
第一个%d吃掉1 2 3,遇到空格,这个空格对于%d是非法字符,只能吃掉数字的%d不可能接受这个空格,第一个%d的读入便到此为止,此时,a拥有了值123,缓冲区中剩余空格 4 5 回车 。
第二个%d开始开始读取,它首先遇到空格,由于%d拥有忽略前导空白符的性质 。他会忽略所有连续的空白符,直到遇到它想要的数字字符为止 。所以第二个%d忽略了前导(缓冲区最前面的)空格,此时第二个%d还没有读到任何东西,缓冲区剩余为 4 5 回车 。
第二个%d吃掉4 5 ,其后是回车,这个回车对于%d是非法字符,只能吃掉数字的%d不可能接受这个回车,第二个%d的读入便到此为止,a=123,b=45,缓冲区剩余回车,格式串中所有的字符都从缓冲区找到了东西与自己对应,这句scanf运行完成 。
注意,并不是什么都能忽略前导空白符,%c是不会忽略前导空白符的 。

详解scanf输入的一些问题

文章插图
遇到非法输入,整句scanf停止运作
有代码
int a=99,b=99,c=99,d=99;d=scanf("%d%d%d",&a,&b,&c);printf("a=%d,b=%d,c=%d,d=%d",a,b,c,d);
键盘输入1 空格 a 空格 1 \n,试图把1赋值给a,'a’赋值给b,1赋值给c,d是scanf的返回值,代表这句scanf成功赋值的个数 。
显然,'a’不可能赋值给int类型的b,现在的问题便是,为b赋值失败,接下来的c能不能接受最后的1,还是说整句scanf因为b遇到了非法输入而就此罢工(提前返回) 。
看图,答案很简单,是后者 。
也就是说,当scanf的格式串的任何一部分遇到了非法输入,整句scanf都会停止运作(排除%d等可以忽略前导空白符的情况) 。