一、scanf函数解析
缓冲
scanf存在缓冲:通过scanf由键盘输入内容时,并不是直接赋值给变量,而是送给缓冲,缓冲可以接受从键盘输入的很多内容。当执行到scanf时,scanf就会从缓冲内读取数据,(那么问题来了)scanf怎么知道读到哪结束?那就需要空白符(空白符:指空格符、制表符、回车符)来控制了 ;当scanf读到空白符时,就停止读取,将空白符前面的内容赋值给scanf中的变量。
简单点关系如图:
当你的程序执行到scanf时,会从你的缓冲区读东西,如果缓冲区是空的,就阻塞住,等待你从键盘输入。
下面举个荔枝:
现在假设你的缓冲区里有:abcd\n1234\n (其中\n是回车符)执行:scanf("%s",name);的时候,由于scanf是读数据直到看见空白符(空白符:指空格符、制表符、回车符)就停止的输入函数。所以执行后,把abcd存到了name中。缓冲区于是变成了 : \n1234\n
接下来的执行就有问题了,如果遇到了:scanf("%d",&number);怎么办?因为遇到了回车符,它并不是一个数字,所以scanf还有一个特性,就是忽略先导的空白符。不管是有几百个回车也好,几万个空格也罢,只要它们连续地出现在缓冲区的开头,就统统忽略他们。然后再读有意义的字符。于是1234被读入number。
当缓冲区还是:\n1234\n的时候,如果遇到了:scanf("%c",&sex);(PS:注意这里的%c不一样)应该怎么办呢?你说,那好办呀,不是说了忽略前导空白符吗?跳过回车读’1’呀!想法是好的,可这只针对你的程序这一种情况。如果我编写的程序就是统计用户输入了多少个回车呢?所以对scanf来讲跳过前导空白符有个例外,当参数是%c的时候,就把缓冲区的第一个字符返回回去,不管是什么。
这样的设计就有个问题,scanf对不同的参数表现出来的特性不一样。得承认,这是个缺陷,但不是说这样不好。
这样的设计至少把发现所有字符的机会交给了用户,设计者这样想:如果程序员使用了scanf("%c",…),那他就有必要知道这函数能把回车符读出来,至于程序员对回车符感不感兴趣,那就看他了,不感兴趣的话,程序员也一定知道该怎么处理。回到你的程序里。
二、验证
1、缓冲的存在,可存放很多内容
下面上一道题,实例说明
代码实现
#include<stdio.h>
int main()
{
int m,i,j,g=1;
double sum=0;
while(~scanf("%d",&m))
{
for(i=1;i<=m;i++)
{
int x;
scanf("%d",&x);
for(j=1;j<=x;j++)
{
double k;
k=1.0/j*g;
sum+=k;
g=(-1)*g;
}
printf("%.2lf\n",sum);
sum=0;
g=1;
}
// printf("\n");
}
}
进行测试
咦!这好像和题目要求不符>.<
那再看看下面这个
是不是和题目一样了呢0.0
解析:其实这就证明了缓冲的存在和作用;第一次测试是执行了两次scanf,并两次从键盘进行输入;而第二次测试是一次性输入两次scanf所需要的值,只需一次键盘输入即可,其实也就是放到缓冲里,执行了两次的scanf都从缓冲里读取数据。(PS:要想实现第二次测试,输入数据一定要配合空白符的使用,这样scanf知道要每次所要读取的数据)
2、scanf("%d\n",&num);
首先%\n是输入的格式,\n是空白符,当从键盘输入时要按照该格式输入;
荔枝如下:
所以以下只要输入空白符就行(不管是空格还是换行)
如果缓冲区没有东西scanf就会阻塞在那里,直到用户有输入
以至于有时候会遇到再输入一行才能有返回的情况。(所以本例中多输入一个1)。
以上部分内容引自(http://blog.pfan.cn/katwood/53198.html)