最近有朋友问了我一个代码,出现了一个bug我也是第一次见这样的写法,研究了半天,觉得有所收获,所以写篇文章分享一下scanf()函数中为什么不用使用\n,以及scanf()函数使用的注意事项。
#include <bits/stdc++.h>
using namespace std;
int main()
{
int day;
scanf("%d",&day);
int sum1=0,sum2=0,sum3=0;
while(day--)
{
int ydj,edj,sdj;
scanf("%d %d %d\n",&ydj,&edj,&sdj);
sum1=sum1+ydj;
sum2=sum2+edj;
sum3=sum3+sdj;
}
printf("%d %d %d %d",sum1,sum2,sum3,sum1+sum2+sum3);
return 0;
}
可以看到 这段代码在scanf("%d %d %d\n",&ydj,&edj,&sdj);中多加了一个\n,那么这就会让代码出现bug,当我们输入3时,没有bug的情况应该输入3行3列数据,然后输出。但是出现bug以后代码就变成了要输入4列才会输出。
如果这段代码不够直观,那我们再来看一段代码,这一段代码在scanf()函数里同样加入了\n;
#define _CRT_SECURE_NO_WARNINGS 1 #include <stdio.h> int main() { int a1, a2, a3; scanf("%d %d %d\n", &a1, &a2, &a3); printf("%d %d %d\n", a1, a2, a3); }
运行会如下所示,会多出一行输入数据4 5 6
但实际上,最后一行即使输入一个数据也可以。
但,总而言之,无论输入几个数据,我们都会多输出一行。
那么我们来分析一下出现这种现象的原因,首先我们先看scanf函数的用法,scanf("非输入控制符 输入控制符",输入参数);
ps:输入控制符是形如%d这样的,非输入控制符是空格之类的,不懂的自行百度,在此我不多赘述。
scanf函数会将从键盘输入的字符转换为输入控制符所规定的数据,然后存入以为输入参数的值为地址的变量中,非输入控制符必须原样输入。比如scanf("%d %d %d",&a,&b,&c);在scanf()函数内我们使用了非输入控制符空格,那么我们在输入时也必须使用空格。
再比如scanf("m%d m%d m%d");这里的%d就是输入控制符,就是非输入控制符,注意%d后面还有一个空客,这是非输入控制符,但是这些非输入控制符要严格按照格式输入。
下面看一个例子。
可以看到我们没有按照输入格式输入,就会得到垃圾值,正确的输入应该为m1 m2 m3
所以 scanf("%d %d %d\n", &a1, &a2, &a3);这段代码如果我们在输入时按照格式输入,正确输入的格式应该为1 2 3\n
可以看到这样输入的话就不会让我们多输出一行。所以回到最初的这个代码,scanf("%d %d %d\n",&ydj,&edj,&sdj);出现多输入一行的原因,是我们没有按照scanf()函数的标准用法去使用。此时,这里\n会被当做非输入字符处理,当我们输入数据时,如果没有从键盘上输入\n,而是直接回车,回车的字符就是\n,此时第一次输入数据之后的回车就是被当做非输入字符处理,scanf函数是在缓冲区\n前面拿值,此时缓冲区就没有\n了,当我们没有输入\n第一次按下回车之后,光标会移动到下一行,等着我们给他一个\n,但是直接按回车健是不行的,虽然回车键是\n,因为此时系统是等我们输入一个值,直接按回车是不行的,编译器会认为我们什么也没输入,编译器是想让我们输入一个数。所以此时我们要从键盘上输入\n,或随便输入一个与输入字符匹配值,这里是%d,按下回车之后就会正常进入下一个语句,但是此时我们多出了一个数据,他会被放到缓冲区,会被下一个scanf()函数使用,这里涉及到缓冲区的问题,不懂的可以去看我的另外一篇关于缓冲区的文章。
下面详细解释一下最开始的那段代码,详细过程都在代码注释里面
#include <stdio.h> int main() { int day; scanf("%d", &day); int sum1 = 0, sum2 = 0, sum3 = 0; while (day--) { //1 2 3\n //4 5 6\n //7 8 9\n //10 11 12\n // scanf("%d %d %d\n") //第一次输入 1 2 3\n(这里的/n会按照非输入字符处理)所以换行之后会让我们继续输入,事实上这里第一次回车之后 //我们输入\n,就会正常结束,但是这里我们继续输入4 5 6\n(这里的\n是scnaf()函数的结束标志); //拿走1 2 3,剩下4 5 6\n;因为前面是%d,所以会拿走3个%d的数字,并没有拿走\n。\n是被当作非输入字符串处理 //第二次输入 7 8 9\n,缓冲区放的是4 5 6\n 7 8 9\n //拿走4 5 6\n ,剩下7 8 9\n //第三次输入10 11 12\n,缓冲区放的是 7 8 9\n 10 11 12\n //拿走7 8 9\n,剩下10 11 12\n int ydj, edj, sdj; scanf("%d %d %d\n", &ydj, &edj, &sdj); sum1 = sum1 + ydj; sum2 = sum2 + edj; sum3 = sum3 + sdj; } int a, b, c; //缓冲区剩下下10 11 12\n 在按一次回车,缓冲区是10 11 12\n\n //拿走10 11 12\n,缓冲区剩下\n scanf("%d %d %d", &a, &b, &c); printf("a= %d b = %d c = %d\n", a, b, c); printf("%d %d %d %d\n", sum1, sum2, sum3, sum1 + sum2 + sum3); //测试缓冲区剩下的数据 char ch = getchar(); if (ch == '\n') { printf("hhhhh\n"); } //输入a printf("请继续输入"); char hh = getchar(); //输出97,说明之前的缓冲区没有数据了 printf("hh =%d\n", hh); return 0; }
运行截图
总结&结语
1在使用scanf()函数之时最好提示用户怎么样输入
2在scanf()函数内尽量不要使用非输入控制符,尤其时\n,
好了以上是本次博客更新内容,如有错误欢迎批评指正。如有疑问欢迎在评论区留言,期待您的关注和点赞,我们一起学习共同进步。