☰
看透标准输入缓冲区:scanf所有疑难问题的总根源
2026/10/17 6:53:17 网站建设 项目流程

先记住一句话:
scanf 不是直接从键盘读,是从「标准输入缓冲区(stdin缓冲区)」里读。
键盘只是往缓冲区里放东西,scanf从缓冲区取东西。

标准输入缓冲区(行缓冲)

Windows、Linux下默认:stdin是行缓冲。

当你在键盘敲字符,不会立刻送给程序,全部先将其放进一块内存区域:输入缓冲区,直到你按下 Enter(回车 \n ),这一整行(包括最后的 \n )才一起送入缓冲区,交给C程序。

举个例子:
输入: 12 3a ,然后按回车。
缓冲区里存的是:
‘1’,‘2’,’ ‘,‘3’,‘a’,’\n’

关键点:

  1. 回车 \n 也是一个普通字符,留在缓冲区里;
  2. scanf读完想要的数据,没用完的字符(空格、回车、字母等)会继续留在缓冲区,下一次scanf接着读;

scanf 整体工作流程

一共三大步,循环执行:

  1. 拿格式串里下一个“东西”(要么是占位符如%d、%c、%s,要么是普通字符/空白字符:逗号、空格、\n、\t)
  2. 从缓冲区里拿字符,尝试和它匹配
  3. 匹配成功→继续;匹配失败→立刻停下,不再往后处理;把没匹配上的那个字符放回缓冲区,下次还能读到;最后返回成功匹配了多少个数据项。

注意:scanf 从来不删除缓冲区里多余的东西,只是“读过”;只有被成功匹配消耗掉的字符,才真正拿走。剩下的还在缓冲区存放着。

函数scanf中占位符与空白字符的功能

占位符

scanf中占位符例如:%d、%c、%f等会跳过空白字符读取对应的数据,例如:

intmain(){inta=0;intb=0;scanf("%d%d",&a,&b);//输入为 (有多个空格)2 3printf("%d,%d",a,b);//输出为2,3return0;}

特殊1:%c什么字符都不跳过例如:

intmain(){chara='z';scanf("%c",&a);//输入enter(\n)printf("%c",a);//输出为一个空白行表示enter被打印return0;}

空白字符

空白字符在输入时是一个字符,但在读取时表示跳过「0个或任意多个连续的空白字符」,一直读到第一个非空白字符才停止。
例如:

intmain(){inta=0;intb=0;scanf("%d%d\n",&a,&b);//输入2 3时敲回车键不会打印,但当你再输入一个非空白字符时则会打印printf("%d\n",a);printf("%d",b);return0;}

特殊2:%s先自动跳过空白,然后一直读,直到再次碰到空白(空格/回车)就停止,并且自动在末尾补’\0’;
不读空白,空白留在缓冲区;容易越界,要用 %19s 限制长度

intmain(){charstr[20];scanf("%s",str);//输入 (有多个空格)Hello worldprintf("%s",str);//输出只有Helloreturn0;}

举例

1.关于输入与格式串不匹配

intmain(){inta=0;scanf("%d",&a);//输入cprintf("%d",a);//输出为0/*1. int a = 0; → 内存里: a = 0 2. 缓冲区来了: 'c','\n' 3. %d 准备读整数:第一个字符是 c ,不是数字 → 匹配失败,立刻结束,不往a里写任何东西! 4. scanf 返回值是 0(成功读到的数据项个数为0); 5. a 仍然是0 → printf打出0。*/return0;}
intmain(){inta;scanf("%d",&a);//输入cprintf("%d",a);//输出为-858993460,这时scanf失败,a没被赋值,里面是内存里的随机垃圾值,就会输出一个莫名其妙的大数。return0;}
inta=0;intb=0;scanf("%d%d",&a,&b);//输入为2cprintf("%d%d",a,b);//输出为20/*输入: 2c ,然后回车。 缓冲区里: '2' , 'c' , '\n' 1. 处理第一个 %d: - 读到 '2' ,是数字,成功,把 2 存入a。 - 下一个字符是 'c' ,不是数字,第一个 %d 停下。 - 注意:%d会把那个'c'放回缓冲区,不动它。 2. 轮到第二个 %d: - 现在缓冲区第一个字符就是 'c' 。 - 'c' 不是数字 → 匹配失败 scanf直接终止 - 不会去碰变量b,b仍然保持初始值0 - 'c' 还留在缓冲区里 - scanf的返回值是 1(只有1个数据项成功匹配) 3. printf输出a → 2,b → 0。*/

因为匹配成功→继续;匹配失败→立刻停下,不再往后处理;把没匹配上的那个字符放回缓冲区,下次还能读到;最后返回成功匹配了多少个数据项。

2.输入中含有普通可见字符时,scanf的格式串应与之一一对应,例如:

intmain(){intyear=0;intmonth=0;intday=0;charch1,ch2;scanf("%d%c%d%c%d",&year,&ch1,&month,&ch2,&day);//输入是2026/10/15printf("%d - %d - %d",year,month,day);//输出结果是2026 - 10 - 15return0;}

执行顺序:

  1. %d → 读数字 2026 → year=2026

  2. %c → 什么字符都不跳过, 把那个 / 读进 ch1(专门用来“接住斜杠”,我们后面不用它)

  3. %d → 读 10 → month=10

  4. %c → 把第二个 / 读进 ch2

  5. %d → 读 15 → day=15

3.当一个字符匹配不上被保留在缓冲区被下一个scanf输出

inta=0;intb=0;charc='z';scanf("%d%d",&a,&b);printf("%d%d",a,b);scanf("%c",&c);printf("%c",c);//输入为2d,输出为20d/*输入: 2d ,然后回车 缓冲区里: '2' , 'd' , '\n' 1. 第一个 %d :读到 '2' , a=2 。下一个字符是 'd' 。 2. 第二个 %d :拿 'd' 尝试读整数 → 匹配失败 b 还是初始值0; - 'd' 留在缓冲区的里面 4. 然后直接跑到: scanf("%c", &c); 这里没有等你再敲任何东西,没有第二次输入 它直接从缓冲区把那个遗留的 'd' 读走,放进变量c里! 5. 打印c:输出 d 。*/

运行关闭

当程序正常结束之后,缓冲区就直接被销毁、清空了,里面剩下的字符就彻底没了,不会保留到下一次重新运行程序。

欢迎指正交流!!!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询