scanf的缓冲区问题

Posted 望北i

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了scanf的缓冲区问题相关的知识,希望对你有一定的参考价值。

在写图书馆管理系统的时候,利用scanf输入函数时,莫名其妙把自己用死了。
这里用简单的代码来描述我遇到的问题

#include<stdio.h>
int main(){ 
	int a, b;
	char c;
	printf("输入两个整数"); 
	scanf("%d %d", &a, &b);
	printf("输入一个字符"); 
 	scanf("%c", &c);
 	printf("%d %d %c\\n", a, b, c);
}

输入的格式为: 1 2 c
输出为:在这里插入图片描述
当我输入完 1 2后,本应该在输入字符a可结果就显示成这样。
这就是典型的scanf缓冲区问题

当我们输入完 1 2后按下回车键,输入缓冲区里存放着1 2 \\n,第一次scanf读走1给a,2给b,中间的空格丢弃,此时第一句scanf已经读完。但是缓冲区里还留着一个\\n,当第二scanf读入时,首先检查缓冲区,发现缓冲区里还有\\n,而且正好匹配%c,于是直接读走\\n给c。
关于缓冲区(我遇到的这个问题显然时行缓冲区)
缓冲区又称为缓存,它是内存空间的一部分。也就是说,在内冲空间预留了一定的储存空间,这些储存空间用来缓冲输入或者输出的数据,这部分预留的空间就叫做缓冲区。
缓冲区根据其对应的时输入还是输出,分为输入缓冲区和输出缓冲区。
缓冲区时一块内存区,它在输入输出设备和cpu之间,用来缓存数据。他是低速的输入输出设备和高速的cpu能够协调共工作,避免低速的输入和输出设备占用cpu。
缓冲区分为三种类型:全缓冲,行缓冲和不带缓冲

  1. 全缓冲:当填满标准I/O缓存后才进行实际的I/0操作。其经典代表就是对磁盘文件的读写。

  2. 行缓冲:当输入和输出中遇到换行符,执行真正的I/O操作。这时,我们输入的字符优先存放在缓冲区,当按下回车键换行时,才进行实际的I/O操作。其经典的标准输入(stdin)和标准输出(stdout)

  3. 不带缓冲也就是不进行缓冲,标准出错情况stderr时其典型代表,这使得出错信息可以直接尽快显示出来。
    C对stdin、stdout和stderr的缓存特征没有强行的规定,以至于不同的系统可能有不同的stdin、stdout和stderr的缓存特征。目前主要的缓存特征是:stdin和stdout是行缓存;而stderr是无缓存的。
    缓冲区的大小
    如果我们没有自己设置缓冲区的话,系统就会默认为标准输入输出设置一个缓冲区,这个缓冲区的大小通常时512个字节的大小。
    缓冲区大小由stdio.h头文件中宏BUFSIZ定义
    缓存区的刷新
    下列情况会发生缓冲区的刷新:

  4. 缓冲区满

  5. 缓冲区遇到\\n的时候

  6. 程序结束

  7. 使用特定函数刷新缓存区
    c语言中在读取键盘数据时,一般时带缓存的数据输入,需要回去键才能完成该行数据的输入确认
    而scanf()函数对这个回车确认符并不进行处理,回车符会留在输入缓冲区中,因此,在下一个读‘字符’操作函数(getchar(),scanf(“%c”),gets())运行时,会读到这个字符,而在读取值型数据或者字符串时,scanf()会从第一个非空白字符(空白字符指:回车,空格,TAB键)开始读取,自动忽略前面的空白字符,而遇到空白字符结束该类型数据的输入。
    解决方法

  8. 用fflush(stdin)命令强行刷新输入缓存,丢弃输入缓存中的数据

#include<stdio.h>
int main(){ 
	int a, b;
	char c;
	printf("输入两个整数"); 
	scanf("%d %d", &a, &b);
	printf("输入一个字符");
	fflush(stdin);  //不管缓存中有没有数据,强行清除
 	scanf("%c", &c);// //这里用户输入一个字符
 	printf("%d %d %c\\n", a, b, c);//结果没有问题 
}
  1. 前面有读数据操作,现在要执行读字符操作,则可用getchar()来吃掉前面的回车确认
#include<stdio.h>
int main(){ 
	int a, b;
	char c;
	printf("输入两个整数"); 
	scanf("%d %d", &a, &b);
	printf("输入一个字符");
	getchar(); //吃掉回车确认符
 	scanf("%c", &c);// //这里用户输入一个字符
 	printf("%d %d %c\\n", a, b, c);//结果没有问题 
}
  1. rewind(stdin)清除标准输入的按键缓冲区。rewind函数是把指定流的读写指针重新指向开头
#include<stdio.h>
int main(){ 
	int a, b;
	char c;
	printf("输入两个整数"); 
	scanf("%d %d", &a, &b);
	printf("输入一个字符");
	rewind(stdin) ;//是把文件指针回绕到文件起始处。
 	scanf("%c", &c);// //这里用户输入一个字符
 	printf("%d %d %c\\n", a, b, c);//结果没有问题 
}

总结:
在网上看了很多关于scanf缓冲区的问题所总结出来的,scanf还有很多其他用法。

以上是关于scanf的缓冲区问题的主要内容,如果未能解决你的问题,请参考以下文章

使用缓冲区而不是直接对整数使用 scanf 函数的原因

使用 scanf_s 时出现缓冲区溢出问题

Linux中处理循环中scanf引起的缓冲区清除问题

c语言中用了scanf语句就输不出结果把scanf删掉就能出结果是为什么

scanf和cin

Scanf未在C中执行[重复]