跳到主要内容

scanf函数的那些坑

1. scanf() 跳过输入

问题复现

先看这段简单的代码块,我试着将scanf()写成了两行,接着,就出现了意外的情况。程序在第一个输入完成后直接就结束了,跳过了第二个输入和输出。

scanf("%d",&a);
printf("%d\n",&a);
scanf("%c",&b);
printf("%c\n",&b);

原因分析

那么是为什么呢?在多方查询资料后,发现scanf()函数有一个特性,在接受的类型不是char的时候,遇到换行就会停止。也就是会把除了回车之外的其他字符从输入缓冲区中读入。将回车留在了输入缓冲区。而下面的**scanf()**函数接受的是一个字符,这就导致了scanf()直接读入了换行符(\n,回车),导致了输入就像被跳过了一样。

解决方案

使用getchar()函数来接受这个\n或者用rewind(stdin) 来清空输入缓冲区。

拓展

行缓冲:在这种情况下,当在输入和输出中遇到换行符时,将执行真正的IO操作。这时,我们输入的字符先存放到缓冲区中,等按下回车键换行时才进行实际的IO操作.典型代表是标准输入缓冲区(stdin)和标准输出缓冲区(stdout)。

2. scanf()的返回值

问题复现

在正常的情况下,scanf()的返回值应该是成功读取的项数。在读取不成功的情况下返回值为0,并且在输入EOF的时候,会返回-1。利用这点特性,理论上可以实现以下的输入操作:

while(scanf("%c",&input) != EOF)//EOF被宏定义为-1
//也可改为 while(scanf("%c",&input) == 1)
{
pirntf("%c",input-32)//通过对ASCII码操作实现大小写转换
}

但是在实际运行的时候,哪怕直接输入换行符,这里也不会结束循环。因为这里%c会接受换行符进去。所以这里直接换行并不会结束循环,而会进入下面的循环,并且输出一个错误的结果。

原理分析

如前面所说,scanf的返回值要为0,前提要求是读取要不成功。比如,

while(scanf("%d",&input) != EOF)//EOF被宏定义为-1
//也可改为 while(scanf("%c",&input) == 1)
{
pirntf("%d",input)
}

这里如果输入字母或者其他非数字的值,就会导致scanf出错。也就是返回值为0。此时就可以正常的结束掉这个循环了。

但是在上面的例子中,由于我们期望输入的是一个字符,而换行符也是一个字符,所以这里并不会引发异常。换行符会被直接当作字符被接收并且读入到指定的内存地址中。所以我们在这里不能不输入任何东西直接换行来结束循环。若要结束循环,则需要手动输入EOF。

在大多数情况下(Windows),输入EOF的方式为Ctrl+Z,而在Unix系统中,输入EOF的方式为Ctrl+D。

解决方案

所以这种情况下,由于我的系统环境是类Unix环境,所以只需要按下两次Ctrl+D即可正常的结束输入了。

拓展

那为什么在某些在线的刷题网站上可以直接使用

while(scanf("%d",&input) != EOF)
{
...
}

来处理不确定个数的输入呢?

这跟这类在线刷题网站的工作方式有关。在编译的时候,他们通常会修改我们的输入函数,将其改为文件输入。也就是重定向IO流到文件IO。而在这里,EOF就会真正的被传入进去。

EOF的全称是End-Of-File,用途与名字一样,是用来定义文件的结尾的。