我在网上找了不少资料,但对于rewind(stdin)为什么能够清空缓冲区似乎都没有比较详细的介绍。此贴就本人已知的知识对此过程进行探讨。
首先说一下rewind的定义:rewind 函数将与流 关联的文件指针 重新定位到文件的开头。 (该定义来自微软文档)
那么rewind(stdin)就是将stdin(标准输入流)的指针指向该流文件的开头。
在进行接下来的讨论前,为了更好地理解,我们需要先了解一下缓冲区的部分知识:
系统缓冲区是一个 buffer,用指针维护着这个buffer数据,当你读走一个数据时候,指针肯定后移了一个位置,但是数据还是在的,除非再有数据进入缓冲区覆盖掉这个位置上的数据。但是,这些维护buffer的指针值我们肯定访问不到,因此,计算机读走一个数据后,即使数据是还存在的,我们也无法再访问了,等同于数据被读走后就不在了。
如此,一些问题就解释得通了,例如执行一下代码
#include<stdio.h>
int main()
{
char a,b,c;
scanf_s("%c", &a, 1);
rewind(stdin);
scanf_s("%c", &b,1);
printf("%c",b);
rewind(stdin);
while (scanf_s("%c", &c, 1)==1)
{
printf("%c", c);
}
}
如果输入
123456回车
789
程序只会输出789
这是因为将123456以及回车符’\n’放进缓冲区后,scanf_s检索缓冲区,发现第一个数据符合格式%c后就将其读入,然后缓冲区的内部指针向后移一位,即将指针指向缓冲区中的2(注意,被读的字符‘1’并不会被删除或者清空,上文已经对此做出了解释)。如果没有rewind(stdin),那么下一个scanf就会读取2,并赋值给变量b,但如果有rewind(stdin),那么指针将被调向数据‘1’,再输入数据会将该数据覆盖掉,这也是为什么会输出7而不是2的原因。
但为什么代码后面的循环没有把未使用的23456输出呢?这是因为stdin标准输入流中并非只有一个指针,而是双指针控制输入输出,为了更好地说明问题,用图示进行说明(P start代表起始指针,P end代表结束指针):
输入:123
缓冲区中的结果:
当我们print输入流中的数据时,计算机就会将开始指针到结束指针间的数据进行输出。
在输出结束后两个指针将会在下一内存单元“汇合”
当我们再次输入数据时,起始指针不动,结束指针后移
例如:输入 45678
注意到缓冲区无法再存储数据,两个指针将再次回到起点
而此时再输入数据abc时,将会覆盖先前的数据:
这是在一般情况下缓冲区的处理情况,而我们一旦使用了rewind(stdin)则不管后面是否填满缓冲区,直接将两个指针置至起始处:
输入123 后调用 rewind(stdin)
以上就是rewind(stdin)能清空缓存的原因啦~
但是这种方法并非对所有情况都适用,例如在PTA或者其他刷题平台做题的时候,会发现这样的操作会导致你的数据无法按预期输出,这是因为输出涉及到输出文件,而使用rewind将把文件指针置到起始处,再次输入数据时将会覆盖先前的数据。
所以最好还是使用“笨”办法进行缓冲区的清理:
while ((ch = getchar()) != '\n' && ch != EOF);
最后提一句,fflush方法在许多环境下已经不支持清空缓存咯,详情可以查阅相关博文,这里就不多做赘述了。
小叨叨:其实这篇文章在一年前就发表过,不过因为当时本人学识尚浅,有不足之处,如今再次发表,添加了一些解释与补充,希望对你的理解能有帮助!如果文章有其他疏漏,请不吝赐教!