C语言scanf函数输入时键盘缓冲区\n的问题[经典问题]

转载 2015年07月06日 21:57:01

程序时对scanf在键盘缓冲区留下的字符有疑问,思考不果。看了百度百科上的scanf词条,说scanf输入遇到空格、跳格、回车才会从缓冲区往变量送字符。于是自己写了以下几个程序思考,还是不果。


程序1

#include "stdio.h"


void main()

{

 char a;

 char b;

 scanf("%d",&a);

 scanf("%d",&b);

 printf("%d %d",a,b);

}


键盘输入

97<回车>96<回车>


输出

97 96


问题1:调用第一个scanf输入时,键盘缓冲区所有的字符为97\n,遇到回车,所以缓冲区把97赋值给a。调用第二个scanf输入时,键盘缓冲区所有的字符为96\n,遇到回车,所以缓冲区把96赋值给b。以上我的分析对吗?


程序2

#include "stdio.h"


void main()

{

 char a;

 char b;

 scanf("%c",&a);

 scanf("%c",&b);

 printf("%d %d",a,b);

}


键盘输入

9<回车>


输出

57 10


问题2:调用第一个scanf输入时,键盘缓冲区所有的字符为9\n,遇到回车,所以缓冲区把9赋值给a。调用第二个scanf输入时,键盘缓冲区所有的字符为\n,遇到回车,所以缓冲区把\n赋值给b。以上我的分析对吗?如果对,那程序1中调用第一个scanf时,又为什么不是把97赋值给a后,将\n赋值给b呢?为什么调用第二个scanf时还需要继续输入96<回车>来对b赋值?调用第一个scanf输入时留在缓冲区的\n去哪里了?无端消失了?


程序3

#include "stdio.h"


void main()

{

 char a[100];

 char b[100];

 scanf("%s",a);

 scanf("%s",b);

 printf("%s %s",a,b);

}


键盘输入

abc<回车>def<回车>


输出

abc def


问题3:从输出结果可以看出,字符数组a和字符数组b都在同一行输出。所以字符数组a的值为{‘a’,’b’,’c’},不是{‘a’,’b’,’c’,’\n’}。字符数组b的值为{‘d’,’e’,’f’},不是{‘\n’,‘d’,’e’,’f’},也不是{‘\n’,‘d’,’e’,’f’,’\n’}。以上的分析对吗?如果对,那调用第一个scanf输入时留在缓冲区的\n去哪里了?还有第二个scanf留下\n呢?


程序4

#include <stdio.h>

void main()

{

 int i;

 char j;

 for(i=0;i<2;i++)

  scanf("%c",&j);/*注意这里%前没有空格*/

 printf("%d",j);

}


键盘输入

1<回车>


输出

10


程序5

#include <stdio.h>

void main()

{

 int i;

 char j;

 for(i=0;i<2;i++)

  scanf(" %c",&j);/*注意这里%前有一个空格*/

 printf("%d",j);

}


问题4:程序4应该就像程序2那样,最后把\n(ASCII值为10)赋值给j了,所以输出10。但程序5 scanf里那个空格如何阻止\n给j赋值?想不通,恳请赐教!


程序6

#include "stdio.h"


void main()

{

 int a;

 int b;

 scanf("%c",&a);

 scanf("%c",&b);

 printf("%d %d",a,b);

}


键盘输入

1<回车>


输出

-858993615 -858993654


问题5:这个问题和\n无关的,但写了程序1,却发现了这个问题。我用的是VC6,就算不是VC6,C的任何编译软件里int和char不都是通用的吗?为什么程序1用%d格式能正常获得char型变量,但程序6用%c格式却不能正常获得int型变量?








-----------------------------------------------------分界线-----------------------------------------------



你首先要明白,从键盘读入键盘缓冲区(buffer)的数据都是以ASCII码存储的(包括回车)。

程序1

#include "stdio.h"


void main()

{

 char a;

 char b;

 scanf("%d",&a);

 scanf("%d",&b);

 printf("%d %d",a,b);

}


键盘输入

97<回车>

第一次回车后,buffer中的ASCII:39h,37h,0AH(0A是换行的ASCII), scanf会根据格式字符串中的第一个%d对buffer按字节顺序读取,当读取到0A时,认为%d型的数据结束,此时把已经读取到的39h,37h依据%d转为整型数据97存储在字符型变量a中。(这里是除去了扫描截止点0AH)

此时buffer中已经无任何数据了。


96<回车>

第二次回车后,按同样的流程,scanf会根据格式字符串中的第二个%d对buffer按字节顺序读取。最终b得到96.

此时buffer中已经无任何数据了。

输出

97 96


程序2

#include "stdio.h"


void main()

{

 char a;

 char b;

 scanf("%c",&a);

 scanf("%c",&b);

 printf("%d %d",a,b);

}


键盘输入

9<回车>buffer:39H,0AH

因为scanf会按照第一个%c格式扫描buffer(只扫描一个字节就结束),然后把扫描到的39H直接送到变量a(当以%d格式读出来时,39H就是57)

此时,buffer中只有:0AH。

然后,scanft又遇到第二个%c,继续扫描buffer,得到0aH并送入变量b.

此时buffer中已经无任何数据了


输出

57 10


程序3

#include "stdio.h"


void main()

{

 char a[100];

 char b[100];

 scanf("%s",a);

 scanf("%s",b);

 printf("%s %s",a,b);

}


键盘输入

abc<回车>

第一次回车后,buffer:61H,62H,63H,0AH。

scanf会按照%s的格式对buffer按字节顺序扫描,当扫描到0AH时,结束扫描(按照%s的要求,空格20H也是扫描结束点)。

然后把扫描到的(除去最后一个判断扫描截至的字节0AH)数据直接送入以a为起始地址的字符串。

此时,buffer无任何数据了。


def<回车>

第二次回车后,buffer:65H,66H,67H,0AH.扫描的流程与上面的完全一致。

输出

abc def


程序4

#include <stdio.h>

void main()

{

 int i;

 char j;

 for(i=0;i<2;i++)

  scanf("%c",&j);/*注意这里%前没有空格*/

 printf("%d",j);

}


键盘输入

1<回车>,

这里scanf执行了两次(i==0时,与i==1时),而且每次都是想对j赋值。

第一次scanf,按%c的要求,只扫描buffer中的一个字节,但是buffer中并不数据,于是要求键盘输入数据到buffer,此时的1<回车>代表向buffer中输入了:31H,0AH。

然后按%c的要求,只扫描buffer中的一个字节:31h,并将它直接送入变量j.

此时,buffer中还留下:0AH。


第二次scanf要求键盘输入数据,按%c的要求,只扫描buffer中的一个字节:0Ah,并将它直接送入变量j.

此时,buffer无数据了。


最后,你用%d格式输出j的值(0AH换成整型就是10)


输出

10


程序5

#include <stdio.h>

void main()

{

 int i;

 char j;

 for(i=0;i<2;i++)

  scanf(" %c",&j);/*注意这里%前有一个空格*/

 printf("%d",j);

}

1<回车>2<enter>的情况:

scanf会按照格式控制字符串的要求,顺序扫描buffer.

但是你其中有一个空格,这个很特殊,我也是第一次发现这个问题(一般我都不会在scanf中加入任何常量字符)


我测试了一下:我发现这个空格有吸收回车(0AH)和空格(20H)的“神奇功效”,吸收之后再要求buffer给一个字节,直到这个字节不是0AH或者 20H,此时把这个字节交给下一个格式字串。


第一次循环时遇到格式字串空格,就扫描buffer中的一个字节,但是buffer中无数据,要求从键盘输入数据:1〈回车〉,buffer中有数据了——31H,0AH。再读取到字节31H,scanf发现这个并不是0AH/20H,就把这个字节31H交给格式字符%c处理。

循环结束,此时buffer里面还有:0AH.


第二次循环时遇到格式字串空格,就扫描buffer中的一个字节——0AH,发现是0AH/20H,于是就要求buffer再来一个字节。此时buffer里面已经没有数据了,要求键盘输入:2<enter>.

buffer中有数据了——32H,0AH。于是再读一个字节31H,scanf发现这个并不是0AH/20H,就把这个字节32H交给格式字符%c处理(j最终得到32H)。

循环结束,此时buffer里面还有:0AH.


这里有一篇关于Printf的帖子:http://blog.csdn.net/arong1234/archive/2008/05/18/2456455.aspx


程序6

#include "stdio.h"


void main()

{

 int a;

 int b;

 scanf("%c",&a);

 scanf("%c",&b);

 printf("%d %d",a,b);

}


键盘输入

1<回车>


问题5:


你的编译器VC认为%d数据应该是4个字节,但是你采用的是%c读数据,

 scanf("%c",&a);此句读到的是1的ascii码:31h.然后把31H直接送入地址&a(而并没有改写a的三个高字节地址)。

 scanf("%c",&b);同理。

你可以用printf("a=%x,b=%x\n",a,b);来验证我说的。它们的最低字节肯定是31H,0AH。


PS1:

当你把 int a;int b;放在main()外进行定义时,a,b的初值就是0。此时你会得到正确的结果。

当你把 int a;int b;放在main()内进行定义时,a,b不会被初始化(它们的三个三个高字节地址的内容是不确定的),你就会得到上面错误的结果。(定义的动态变量都不会被初始化,静态变量会被初始化为0)


PS2:以下也是不正确的用法。

char c;

scanf("%d",&c);/当你用%d给c赋值时,会对从&c开始的连续4个字节进行赋值。当从buffer得到的值是在一个字节范围内(-128~127),下面是可以正常输出的。但是不管怎样,这样做是很危险的——越界。

printf("%d",c);

=================请你测试下这个程序========================

#include "stdio.h"

void main()

{

char c[4],i=4;

scanf("%d",c);/*请输入258<回车>*/


while(i-->0)

printf("%02x ",c[i]);

printf("\n");

}/*如果得到的结果是00 00 00 01 02就说明我的结论是正确的(258的转为16进制数就是00 00 01 02H,然后scanf会把这个数放入以c为起始地址的) 


================以下程序也是======================

#include "stdio.h"

void main()

{

char c,i=4;

char *p=&c;

scanf("%d",&c);/*请输入258<回车>*/


while(i-->0)

printf("%02x ",p[i]);

printf("\n");

}

提问者评价


相关文章推荐

在scanf里面使用换行符\n是一种什么体验?scanf("%d\n",&a);

今天有个学生发了简单的求周长程序给我,和我说,不知道为什么输入了4个数,而且最后计算时input long 输入的两个数,和输入的width没有关系了。 课堂强调过scanf里面不要去用\n,我原以...

键盘输入缓冲区与scanf()原理

键盘输入缓冲区与scanf()原理。键盘缓冲区用来缓存“按键”的ASCII码,而scanf()每次从键盘缓冲区中读取一个字符(ASCII码),直到键盘缓冲区为空。如果键盘缓冲区为非空状态,执行scan...

第六篇 键盘中断与应用程序读取键盘缓冲区

这篇博文主要介绍在X86下键盘的中断过程,以及应用程序如何利用中断读取键盘缓冲区内容。   一、撰写该篇博文的背景介绍 在我们全屏看视频时,按下Esc键,播放器还原或者最小化;在利用其他软件的时...

学习笔记5——scanf 为毛要敲回车?------输入输出缓冲区,键盘缓冲区

键盘的内部有一块微处理器,它控制着键盘的全部工作,比如主机加电时键盘的自检、扫描,扫描码的缓冲以及与主机的通讯等等。当一个键被按下时,微处理器便根据其位置,将字符信号转换成二进制码,传给主机和显示器。...

getchar、getch区别与用法 .

getchar()和getch()的问题一直困扰着大家,关于他们的区别也是众说纷纭,可没有一种说法是详细、深入的,今天我就在前人已有的成果上,试着与大家继续探讨下这个问题:    先看看规范点的说法:...

C语言编程 - 清空键盘缓冲区

清空键盘缓冲区很多种方法,如用fflush(stdin); rewind(stdin);等,但是在linux这些都不起作用,还得我今天试了半天都没成功,上网搜了一下发现setbuf(stdin, NU...

C语言中scanf函数与输入缓冲区

讨论下scanf函数,输入缓冲区的关系  样例来源于算法竞赛入门经典第一章实验部分的内容,经过测试发现scanf函数对于整形数据在读入时会过滤掉 空格符 、换行符 和 水平制表符。按照提示,如果b的...

不定参数的应用

不定参数的应用不定参数当年做为C/C++语言一个特长被很多人推崇,但是实际上这种技术并没有应用很多。除了格式化输出之外,我实在没看到多少应用。主要原因是这种技术比较麻烦,副作用也比较多,而一般情况下重...

C语言编程 - 清空键盘缓冲区

转载自这里 清空键盘缓冲区很多种方法,如用fflush(stdin); rewind(stdin);等,但是在linux这些都不起作用,还得我今天试了半天都没成功,上网搜了一下发现setbu...

对C语言输入输出流和缓冲区的深入理解

导读:对C语言输入输出流和缓冲区的深入理解,C语言缓冲区(缓存)详解,缓冲区又称为缓存,这些存储空间用来缓冲输入或输出的数据,这部分预留的空间就叫做缓冲区,缓冲区根据其对应的是输入设备还是输出设备,分...
  • SHRDLU
  • SHRDLU
  • 2015-10-06 10:08
  • 7062
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)