吃掉缓冲区
吃掉缓冲区
背景:
对输入的一个数判断是否是素数。所谓素数是指这个数只能被1和自身整除。要求在主函数输入一个数,调用函数Fun()判断该数是否是素数。打印信息在主函数中进行。例如:从键盘输入5,5是素数则打印如下信息:”5 is a prime number”。又如:从键盘输入4, 4不是素数则打印如下信息:”4 is not a prime number”。负数、0和1均不是素数。对输入的数据要考虑数据的合法性,不满足条件的数要重新输入直到满足条件为止。不能使用全局变量,不按给定的函数原型编写程序
不给分。Fun()函数原型为: int Fun(int m);
**输入数据提示信息:”Please input a number:\n”
注:该提示信息请放在循环体外
**输入数据格式为:”%d”
**输出格式要求:
若是素数输出数据格式为:”%d is a prime number\n”
若不是素数输出数据格式为: “%d is not a prime number\n”
程序运行示例1;
Please input a number:
11
11 is a prime number
程序运行示例2:
Please input a number:
56
56 is not a prime number
代码实现:
1 | |
吃掉缓冲区:
代码实现:
1 | |
一、 什么是“缓冲区” (Buffer)?
想象你在自动售货机买东西。
- 键盘:就是投币口。
- **变量
m**:就是最终存放饮料的地方。 - 缓冲区:就是机器内部的暂存通道。
当你敲击键盘输入 100A 并按下回车时,数据并不会直接飞进变量 m 里。它们会先像排队一样,进入缓冲区(传送带):
缓冲区现在的状态:
Plaintext
1 | |
注意:
\n是你按的回车键,它也是一个字符,叫换行符。
二、 scanf 的原理:一个挑剔的搬运工
scanf("%d", &m) 就像一个负责搬运数字的搬运工。它的工作规则如下:
- 跳过空白:一开始如果遇到空格、换行,它会直接扔掉。
- 贪婪读取:它会一直往后拿字符,直到遇到一个不是数字的字符为止。
- 放回:遇到不认识的字符,它不会拿走,而是留在传送带上。
还原你遇到的 100A 场景:
第一轮循环:
你输入了
100A+ 回车。scanf看到1,是数字,拿走。scanf看到0,是数字,拿走。scanf看到0,是数字,拿走。scanf看到A,不是数字!停!scanf把拿到的100组装好,放进变量m里。返回1(表示成功读到一个数)。重点: 此时传送带上还剩下
A和\n。Plaintext
1
缓冲区剩余:[ 'A', '\n' ]程序继续运行,判断
100不是质数,打印结果。
第二轮循环(死循环的开始):
循环回来,再次执行
scanf("%d", &m)。scanf看了一眼传送带的第一个东西:A。scanf说:“我要的是%d(整数),这是个字母,我不干了。”scanf读取失败,返回0。变量m没被修改(还是旧值 100)。重点: 因为
scanf读取失败,它没有拿走A。Plaintext
1
缓冲区剩余:[ 'A', '\n' ] <-- A 还在!程序往下跑,打印“100 不是质数”。
回到步骤 1,
scanf又看到A…… 死循环达成。
三、 getchar() 扮演的角色:不挑食的清洁工
getchar() 的功能非常简单粗暴:从传送带上拿走一个字符,不管是什么,拿走就是了。
现在看这段“吃掉缓冲区”的代码:
C
1 | |
这句话翻译成白话就是:
“只要拿到的东西不是回车符 (
\n),就继续拿,直到拿到回车符为止。”
加上这段代码后,第二轮循环发生了什么?
scanf发现传送带开头是A,读取失败,返回 0。进入
if判断失败的分支。执行
while(getchar() != '\n');:- 第 1 次
getchar():拿走了A。是不是回车?不是。继续。 - 第 2 次
getchar():拿走了\n。是不是回车?是!循环结束。
- 第 1 次
此时缓冲区状态: 空了!
Plaintext
1
缓冲区:[ ]continue跳回循环开头。第三轮
scanf:传送带是空的,它会乖乖停下来,等待你键盘输入新的数字。
这就是“清空缓冲区”的原理。
四、 解答你的具体疑惑
Q1: getchar 没有地址啊,读取了的东西放哪?
答:被扔进了“垃圾桶”。
在 C 语言中,函数是有返回值的。
- 如果你写
char c = getchar();,意思是把拿到的东西存进变量c。 - 如果你只写
getchar();,意思是调用函数,让它执行“拿走字符”这个动作,但是忽略它的返回值。
就像你买了一瓶水(getchar),但是你没伸手接(没赋值给变量),这瓶水就直接掉在地上流走了。在这里,我们的目的就是让它流走(从缓冲区消失),我们并不关心它具体是 A 还是 B。
Q2: 为什么 100A 会先读 100,下次再读 A?
答:这是 scanf 的“分隔符”机制。 scanf 只要遇到不符合格式规定的字符,就会认为“当前这个数据的读取结束了”。
- 对于
%d,字母就是结束符。 - 对于
%d,空格、回车也是结束符。 正因为如此,你才能在一次输入中用空格隔开多个数字(比如输入10 20),scanf才能分两次读走它们。只不过在你的场景里,A是个意外的非法字符,导致了后面的堵塞。
Q3: if (scanf(...) != 1) 是什么意思?
答:利用返回值的“体检报告”。 scanf 函数执行完后,会返回一个整数,代表它成功读取并填好了几个变量。
- 我们写的是
scanf("%d", &m),只想读 1 个整数。 - 如果成功,它返回
1。 - 如果遇到
A,它读不了,返回0。 - 如果遇到文件结束(EOF),它返回
-1。
所以 if (scanf(...) != 1) 的意思就是:“如果在读取过程中出了岔子(没读到 1 个数),那肯定是有问题了,赶紧进 if 里进行错误处理(清空缓冲区)。”