C语言——行缓冲区与相关的问题

1. 行缓冲区

1.1 概念

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

1.2 结合缓冲区来深度理解getchar

  • 当程序调用getchar()函数时,程序就等着用户按键, 用户输入的字符被存放在键盘缓冲区中,直到用户按回车为止(回车字符也放在缓冲区中) 。
  • 当用户键入回车之后,getchar()函数 才开始从键盘缓冲区中每次读入一个字符 。也就是说, 后续的getchar()函数调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完后,才重新等待用户按键 。
  • 打个比方,键盘缓冲区就像是一条水管连着你的程序,程序调用getchar()函数用户输入字符就相当于往水管里注水,这个水注多少取决于你输入多少,当你按回车停止注水时,getchar()函数才会开始从键盘缓冲区,
  • 也就是我们的水管里取水,那每次只会读一个字符也就是每次取一定量的水,当你在这之后继续调用getchar()函数时,会接着在水管里取上次没用完的水,因为你的水管没清空(缓冲区的刷新),
  • 那这个阶段就不用你再输入了,因为一调用getchar()函数就有水可取嘛,直到水管里没水了,你还调用getchar()函数,那这个时候你就得注水了也就是程序会等你按键。
  • 通俗一点说,当程序调用getchar()函数时,程序就等着用户按键,并等用户按下回车键返回。期间按下的字符存放在缓冲区,第一个字符作为函数返回值。
  • 继续调用getchar()函数,将不再等用户按键,而是返回您刚才输入的第2个字符;继续调用,返回第3个字符,直到缓冲区中的字符读完后,才等待用户按键。
  • getchar()函数的执行就是采用了行缓冲。第一次调用getchar()函数,会让程序使用者(用户)输入一行字符并直至按下回车键 函数才返回。此时用户输入的字符和回车符都存放在行缓冲区。再次调用getchar()函数,会逐步输出行缓冲区的内容
  • 下面我们用代码来验证一下
#include <stdio.h>
int main()
{
   
	char c = getchar();
	printf("%c\n", c);
	system("PAUSE");
	c = getchar();
	printf("%c\n", c);
	system("PAUSE");
	return 0;
}
  • 我们输入1 2 3来试试

在这里插入图片描述

  • 我们会发现第二次输出的并不是2,而是空格,并且第二次我们并没有再输入,直接就读取了,这就印证了缓冲区的存在了

1.3 结合缓冲区来深度理解scanf

1.3.1 问题1

  • 不知大家在学习C语言的过程中有没有过这样的问题:
#include <stdio.h>
int main()
{
   
	int a = 0;
	scanf("%d\n", &a);
	printf("%d", a);
	return 0;
}
  • 当我们输入10并按下回车的时候,发现程序没有结束,而且似乎还在等待我们输入数据

在这里插入图片描述

  • 这到底是为什么呢?
  • 我们先来回顾一下scanf输入的特点是什么?
  1. scanf() 处理用户输入的原理是,用户的输入先放入缓存,等到按下回车键后,按照占位符对缓存进行解读
  2. 解读用户输入时,会从上⼀次解读遗留的第⼀个字符开始,直到读完缓存,或者遇到第⼀个不符合条件的字符为止,并且不会带走不符合条件的这个字符
  3. 并且scanf() 处理数值占位符时,会自动过滤空白字符,包括空格、制表符、换行符等(前提是scanf的参数中没有出现这些字符
  • 从第三条我们会发现现在正好是scanf的参数中出现了换行符,并且我们还会发现如果没有出现这些操作符的时候,都是直接过滤掉的,
  • 那是不是可以理解他们在scanf眼中的地位是一模一样的,所以这里如果把scanf参数中的 \n 换成空格也会出现一样的问题
  • 接下来解释一下具体的原因:

在这里插入图片描述

  • 当我们再输入20的时候:

在这里插入图片描述

  • 确实如此,
  • 此时就会有同学会有疑问,那这样的话,20是不是还留在缓冲区了?
  • 我们加个getchar 来验证一下
#include <stdio.h>
int main()
{
   
	int a = 0;
	scanf("%d\n", &a);
	printf("%d\n", a);
	printf("%c\n", getchar());
	return 0;
}
  • 运行结果为:

在这里插入图片描述

  • 我们发现结果竟然不是20!,是不是我们翻车了呢?
  • 当然不是,因为20在getchar看来是两个字符,所以只读取了2
  • 我们可以再来验证一下:
#include <stdio.h>
int main()
{
   
	int a = 0;
	scanf("%d\n", &a);
	printf("%d\n", a);
	printf("%c\n", getchar());
	printf("%c\n", getchar());
	return 0;
}

在这里插入图片描述

  • 果然是这样,
  • 如果我们再加上一条getchar呢?会发生什么神奇的事呢?
#include <stdio.h>
int main()
{
   
	int a = 0;
	scanf("%d\n", &a);
	printf("%d\n", a);
	printf("%c\n", getchar());
	printf("%c\n", getchar());
	printf("%c\n", getchar());
	return 0;
}

在这里插入图片描述

  • 我们发现空白行多了一行,其实这就是我们按下回车后放在缓冲区的 \n

1.3.1 问题2

  • 接下来我们再利用上面所学的来解释另一个问题
#include <stdio.h>
int main()
{
   
	char arr[10] = "0";
	scanf("%s", arr);
	printf("%s\n", arr);
	char a = '0';
	scanf("%c", &a);
	printf("%c\n", a);
	return 0;
}

  • 我们想先给数组输入一个字符串之后再输入一个字符
  • 但是当我们输入字符串之后,程序直接就结束了,按理来说应该让我们再输入一个字符才对啊

在这里插入图片描述

  • 学了行缓冲区后我们就可以解释其原因了
  • scanf读取完后并不会把回车输入的换行符给带走,而是留在了缓冲区所以当scanf读取的时候缓冲区还有字符,所以scanf不等你输入直接就读取\n了,所以我们发现程序运行后会有三行空行
  • 那应该怎么避免呢?
  • 第一种方法:我们可以在第一个scanf读取完后利用getchar把缓冲区清空
#include <stdio.h>
int main()
{
   
	char arr[10] = "0";
	scanf("%s", arr);
	
	while (getchar() != '\n')
	{
   
		;
	}//利用getchar循环清空缓冲区
	
	printf("%s\n", arr);
	char a = '0';
	scanf("%c", &a);
	printf("%c\n", a);
	return 0;
}
  • 第二种方法:既然scanf有缺陷,那我直接把他换掉不就完了
  • 那换谁了?换gets!输入字符串的时候,gets可以完全代替scanf
  • gets在读取完字符串后会把后面的\n直接拉出来给扔了,并且遇到空格的时候仍然会读取,可以说在读取字符串方面完胜scanf
#include <stdio.h>
int main()
{
   
	char arr[10] = "0";
	gets(arr);
	printf("%s\n", arr);
	char a = '0';
	scanf("%c", &a);
	printf("%c\n", a);
	return 0;
}
  • 运行结果:

在这里插入图片描述
最后,
恭喜你又遥遥领先了别人!

在这里插入图片描述

相关推荐

  1. C++:C语言相比特点

    2024-02-09 22:32:01       34 阅读
  2. C语言】命令

    2024-02-09 22:32:01       17 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-02-09 22:32:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-02-09 22:32:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-02-09 22:32:01       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-02-09 22:32:01       20 阅读

热门阅读

  1. 字符串Hash的一个板子题的思考

    2024-02-09 22:32:01       28 阅读
  2. 贪心+堆维护,LCP 30. 魔塔游戏

    2024-02-09 22:32:01       33 阅读
  3. 人工智能深度学习如何入门?

    2024-02-09 22:32:01       26 阅读
  4. QT时间日期与定时器

    2024-02-09 22:32:01       29 阅读
  5. 第三百一十二回

    2024-02-09 22:32:01       32 阅读
  6. MongoDB聚合: $sortByCount

    2024-02-09 22:32:01       28 阅读
  7. C#系列-数据结构+递归算法+排序算法(3)

    2024-02-09 22:32:01       27 阅读
  8. VUE2和VUE3区别对比一览

    2024-02-09 22:32:01       24 阅读
  9. XGB-5: DART Booster

    2024-02-09 22:32:01       29 阅读
  10. C语言常见面试题:C语言有哪些数据类型?

    2024-02-09 22:32:01       21 阅读