对C语言scanf函数机制的浅探究

对C语言scanf函数机制的浅探究

在使用C语言写程序的时候,往往会使用到scanf()函数,然而如果对这个函数的机制不太清楚,不正确的输入下,产生的结果难以估计。于是通过实验,总结了一下scanf()函数的机制。


格式化字符串匹配失败会直接退出函数运行


int a=4;
scanf("x%d", &a);
printf("%d", a);

如果此时输入了a89,格式化字符串匹配失败,将直接退出函数。变量a的值没有被改变。

int a=4;
scanf("%d", &a);
printf("%d", a);

如果此时输入了字母a,格式化字符串匹配失败,也将直接退出函数。变量a的值也没有被改变。所以这两段代码的输出都是

4

输入缓存区


C语言有输入缓存区,scanf会优先从缓存区读取数据来匹配格式化字符串,输入缓存区为空的话才会向用户请求输入。

注意是为空才请求输入,而不是匹配不成功请求输入

char a;
int b=1;
scanf("%c",&a);
scanf("%d",&b);
printf("输出:%d",b);

例如这串代码,如果第一次请求输入时输入了 ab ,此时字符 ‘a’ 被存入变量a,输入缓存区还剩下 [ 'b' , '\n' ] ,所以第二个scanf函数不会再请求用户输入,而是直接去缓存区匹配,而这次匹配不成功,所以变量b的值没有被改变过。这段代码的输出为

输出:1

使用占位符%d时情况不太一样

scanf("%d");

我们在使用%d接受数字输入时,发现一种情况,即使直接输入回车,也不会匹配不成功,而是一直等待用户输入,直到输入含有空格回车之外的字符传入。

为什么呢?考虑这种情况:scanf("%d%d", &a, &b),用户会在输入第二个数字前先打上空格或者回车,所以设计了这种情况。

就是说,C语言按%d接受输入时,会把匹配到数字之前匹配到的空格和回车看作分隔符拿出缓存区扔掉,不作匹配使用,继续请求用户输入。

再看刚才的代码,分析一下。如果输入a,此时字符 ‘a’ 被存入变量a,输入缓存区还剩下 [ '\n' ] ,所以这种情况下,即使缓存区还有一个字符,可因为它是换行符,按%d输入时依旧会把它看作分隔符,此时还是会向用户请求输入。


格式化字符串的匹配机制–> 逐字符匹配


如果我们想要以a-b的形式输入系数-指数以此存储一个多项式,a-b之间以英文逗号分隔,我们会怎么写呢?

int c, e;
do
{
    scanf("%d-%d", &c, &e);
    /* 省略存储过程 */
} while (getchar()==',');

这么写非常好,但是如果用户没有按给定格式输入时,会发生什么错误我们得心里有数。

比如我输入是:3,4-3,5-,a-5 ,发生了什么呢?

这时候C语言就会采取逐字符匹配的原则来工作。PS:每两次执行之间都会被getchar()函数从缓存区拿走一个 ‘,’ 。

缓存区 执行顺序 匹配值 退出原因 执行操作
3,4-3,5-,a-5\n 1 3 ‘-‘ 与 ‘,’ 不匹配 c=3
4-3,5-,a-5\n 2 4-3 格式化字符串匹配结束 c=4,e=3
5-,a-5\n 3 5- ‘,’ 与%d不匹配 c=5
a-5\n 4 ‘a’ 与%d不匹配

这么看来,即使没有完全匹配到规定的字符串,sacnf函数也会将现有匹配到的先作使用,即使后面匹配失败,前面匹配成功的部分依然有效。

结合前面,我们还可以知道如果输入的是 3, 4- 3, 5-, a-5 (中间多了空格),因为匹配 %d 时会忽略空格回车的机制,即使逗号后面加了空格,和原输入也还是等价的。这么做还是有一些小缺陷,因为输入时即使按照要求,也可能因为习惯,在逗号前加了空格,我们可以这么处理。

int c, e;
char m;
do
{
    scanf("%d-%d", &c, &e);
    /* 省略存储过程 */
    do{ m = getchar(); }while(m==' ');
} while (m==',');

这样处理的代码,即使在分隔符前后都加上空格,我们也能确保它正确处理,代码变得友好了很多。至于其他情况嘛……有精力的话也可以再设计一下,我是不想设计了hhhh,毕竟已经满足以a-b的形式输入系数-指数,以英文逗号分隔的要求了~(不听话的用户活该得不到正确结果!hhhh开玩笑)


其他占位符


其他占位符也有类似的情况,在此不再赘述,想要研究的小伙伴可以自行研究一下!

作者

jiujiu

发布于

2022-03-31

更新于

2024-06-15

许可协议

评论