吃掉缓冲区

吃掉缓冲区

背景:

对输入的一个数判断是否是素数。所谓素数是指这个数只能被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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <math.h>

int Fun(int);

int main()
{
int m;
while (1) {
printf("Please input a number:\n");
/*f(scanf("%d", &m) != 1) {
while (getchar() != '\n');
continue;
}*/
if (scanf(" %d", &m) != 1) {
// 如果读取失败(比如用户输入了字母),需要清空缓冲区
printf("Invalid input! Please enter an integer.\n");
while (getchar() != '\n'); // 吃掉错误的字符
continue; // 跳过本次循环,重新开始
}
if (!Fun(m)) {
printf("%d is not a prime number\n", m);
}
else {
printf("%d is a prime number\n", m);
break;
}
}

return 0;
}

int Fun(int m) {
for (int i = 2;i <= sqrt(m);i++) {
if (m % i == 0) {
return 0;//不是质数
}
}
return 1;
}

吃掉缓冲区:

代码实现:

1
2
3
4
5
6
7
8
9
if (scanf(" %d", &m) != 1) {

// 处理非数字输入的容错代码(这里简单处理:清空缓冲区)

while(getchar() != '\n');

continue;

}

一、 什么是“缓冲区” (Buffer)?

想象你在自动售货机买东西。

  • 键盘:就是投币口。
  • **变量 m**:就是最终存放饮料的地方。
  • 缓冲区:就是机器内部的暂存通道

当你敲击键盘输入 100A 并按下回车时,数据并不会直接飞进变量 m 里。它们会先像排队一样,进入缓冲区(传送带):

缓冲区现在的状态:

Plaintext

1
[ '1', '0', '0', 'A', '\n' ]

注意: \n 是你按的回车键,它也是一个字符,叫换行符。


二、 scanf 的原理:一个挑剔的搬运工

scanf("%d", &m) 就像一个负责搬运数字的搬运工。它的工作规则如下:

  1. 跳过空白:一开始如果遇到空格、换行,它会直接扔掉。
  2. 贪婪读取:它会一直往后拿字符,直到遇到一个不是数字的字符为止。
  3. 放回:遇到不认识的字符,它不会拿走,而是留在传送带上

还原你遇到的 100A 场景:

第一轮循环:

  1. 你输入了 100A + 回车。

  2. scanf 看到 1,是数字,拿走。

  3. scanf 看到 0,是数字,拿走。

  4. scanf 看到 0,是数字,拿走。

  5. scanf 看到 A不是数字!停!

  6. scanf 把拿到的 100 组装好,放进变量 m 里。返回 1(表示成功读到一个数)。

  7. 重点: 此时传送带上还剩下 A\n

    Plaintext

    1
    缓冲区剩余:[ 'A', '\n' ]
  8. 程序继续运行,判断 100 不是质数,打印结果。

第二轮循环(死循环的开始):

  1. 循环回来,再次执行 scanf("%d", &m)

  2. scanf 看了一眼传送带的第一个东西:A

  3. scanf 说:“我要的是 %d(整数),这是个字母,我不干了。”

  4. scanf 读取失败,返回 0。变量 m 没被修改(还是旧值 100)。

  5. 重点: 因为 scanf 读取失败,它没有拿走 A

    Plaintext

    1
    缓冲区剩余:[ 'A', '\n' ]  <-- A 还在!
  6. 程序往下跑,打印“100 不是质数”。

  7. 回到步骤 1,scanf 又看到 A…… 死循环达成


三、 getchar() 扮演的角色:不挑食的清洁工

getchar() 的功能非常简单粗暴:从传送带上拿走一个字符,不管是什么,拿走就是了。

现在看这段“吃掉缓冲区”的代码:

C

1
while (getchar() != '\n');

这句话翻译成白话就是:

“只要拿到的东西不是回车符 (\n),就继续拿,直到拿到回车符为止。”

加上这段代码后,第二轮循环发生了什么?

  1. scanf 发现传送带开头是 A,读取失败,返回 0。

  2. 进入 if 判断失败的分支。

  3. 执行 while(getchar() != '\n');

    • 第 1 次 getchar():拿走了 A。是不是回车?不是。继续。
    • 第 2 次 getchar():拿走了 \n。是不是回车?是!循环结束。
  4. 此时缓冲区状态: 空了!

    Plaintext

    1
    缓冲区:[ ]
  5. continue 跳回循环开头。

  6. 第三轮 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 里进行错误处理(清空缓冲区)。”


吃掉缓冲区
http://example.com/2025/12/02/吃掉缓冲区/
作者
王柏森
发布于
2025年12月2日
许可协议