scanf()函数
1. 缓冲区的概念
缓冲区就是键盘按下回车键后 所输入内容的去处,也是sancf(),getchar()等函数读取数据的来源,这里有两个需要明白:
a. 函数从缓冲区读走字符 和 键盘把键值存到缓冲区 这是两个互相独立的过程,即 你存你的,我取我的
b. 当缓冲区为空的时候,调用sancf(),getchar()等函数会使程序处于等待输入的状态,而如果缓冲区有内容,则直接读取缓冲区,不用等待。
2. scanf()的工作流程
a. 从左到右处理格式串字符
b. 遇到转换说明符(如 %d),scanf( )尝试从缓冲区读取数据(缓冲区为空时,则等待键盘输入)中定位相应类型的项,并跳过必要的空格
c. 读取数据项,并在遇到不可能属于此数据类型的字符时停止
d. 若数据读取成功,则写入对应的变量(通过指针访问变量空间)
e. 继续处理格式字符串的剩余部分
f. 格式字符串处理完毕,结束调用
g. scanf()函数是有返回值的,表示成功读入数据项个数,比如成功读取2次,那scanf()的返回值就是2
注意:scanf()读取数据时会跳过必要的空格,准确的说是在读取非字符类型的数据才跳过,也就是在多个数据之间本来就可以随便加空白字符,对于下面最常见的代码:
#include <stdio.h>
int main()
{
int i,j;
scanf("%d%d",i,j);
printf("i = %d, j= %d\n",i j);
return 0;
}
在输入第一个数之后,可以随便敲任意的空格,回车,换行后再输入第二个数,程序照常运行,即使格式字符说明 %d%d 是紧挨着的
3. scanf()处理空白格式字符串
有时候会遇到 scanf("%d\n%d",i,j) scanf("%d %d",i,j) 的情况这里格式串字符带空白符的写法,是用特殊的用法,并没有那么简单
可能令人吃惊,\n在scanf格式串中不表示等待换行符,而是读取并放弃连续的空白字符。(事实上,scanf格式串中的任何空白字符都表示读取并放弃空白字符。而且,诸如%d这样的格式也会扔掉前边的空白,因此你通常根本不需要在scanf格式串中加入显式的空白。)
—— 《你必须知道的495个C语言问题》- 第12章标准输入输出库
因此 scanf("%d\n%d",i,j) scanf("%d %d",i,j) 中的空白符,并不表示匹配字符,而是重新读取一个非空白字符, scanf("%d\n%d",i,j) scanf("%d %d",i,j) 空白符介于两个数据转换符中间,体现不出来这一奇葩特性(空白符后面本来就需要读取一个非空白字符 %d 数据),但如果空白符位于格式串的末尾,那就有问题了----即:在按照指定方式读取后需要多读取一个非空白字符,具体见如下代码:
#include <stdio.h>
int main()
{
int i;
scanf("%d\n", &i);
printf("i = %d\n", i);
return 0;
}
运行结果:
在输入第一个数 1 之后,格式串字符遇到了 \n 。此时scanf()仍然处于等待状态,需要继续读取下一个非空白字符才会结束;
4. scanf()输入不匹配导致无限循环
问题代码如下
#include <stdio.h>
int main()
{
int i;
scanf("%d", &i);
while(i != 123)
{
printf("输入错误\n");
scanf("%d", &i);
}
return 0;
}
输入是%d类型的数据,程序可以正常运行,但是当你输入字母时,会无限打印 输入错误,是因为 scnaf() 函数遇到不可能属于次数据项类型的字符时停止,由于我们输入的是字母,显然不能匹配 %d ,于是scanf()函数读入失败,什么都没干直接结束,变量 i 的值未变,更为致命的是 非法的字符一直停留在缓冲区中,现在缓冲区已经非空,并且也不匹配 %d ,下次scanf() 从缓冲区直接读取,并且仍然非法,这样就形成死循环
解决办法
利用 scnaf()函数有返回值,来判断数据读取是否成功,再通过getchar()将缓冲区残留字符清空,就可以实现对非法字符的处理,见如下代码:
#include <stdio.h>
//判断输入是否正确
int get_int()
{
int i;
while(1 != scanf("%d", &i))
{
printf("input illegal\n");
while('\n' != getchar()); //清空缓冲区
}
else
return i;
}
int main()
{
int i;
while(i = get_int()) //只有匹配到数字,才可以进入密码比对
{
if(123 == i)
{
printf("good job\n");
break;
}
else
{
printf("password error\n");
printf("input again\n");
}
}
return 0;
}
6. scnaf()处理字符输入
scanf("%c", ch) 和 getchar() 一样,是允许接收任何输入的,也就是会把 你输入的空白符回车 换行也存入缓冲区,这样就容易存在误操作,因为缓冲区中存在空格 回车 换行 都可以被scanf("%c", ch) 和 getchar() 读取,误代码如下:
#include <stdio.h>
int main()
{
char buf[5];
char ch;
int i;
for(i = 0; i < 5; i++)
scanf("%c", &buf[i]);
for(i = 0; i < 5; i++)
printf("%c", buf[i]);
printf("\n");
return 0;
}
运行结果
scanf("%c", ch) 允许接受任何输入,出现这种情况的原因就是 scanf()会读取键入缓冲区中的空格 换行 回车,实际程序想存入的是 a b c d e,如何让scnaf()函数吃掉回车,有以下几种解决办法:
a. 用getchar() 清除
b. 用 “%1s"限制;
c. 在 %c前面加一个空白符(空格 换行符 tab)变成 " %c" 或者 “\n%c” 或者 " %c"来吃掉回车(一个空格变成” %c" 最为美观 );
d. 用fflush(stdin)清楚全部剩余内容;(不建议使用)
第1中方法,利用getchar()将缓冲区中的回车读掉
第2中方法,显示只取一个字符,其他的丢弃
第3中方法,在格式串中加上空白符可以吃掉的回车,因为scanf()格式串中的空白符表示 按照指定方式读取后需要多读入一个非空字符,代码如下
#include <stdio.h>
int main()
{
char buf[5];
char ch;
int i;
for(i = 0; i < 5; i++)
scanf(" %c", &buf[i]); //用空格来吃掉空白符
for(i = 0; i < 5; i++)
printf("%c", buf[i]);
printf("\n");
return 0;
}
第4中方法不建议使用
7.小陷阱
见下面问题代码
#include <stdio.h>
int main()
{
int i, j;
char ch;
scanf("%d%d", &i, &j);
printf("%d %d",i, j);
return 0;
}
类似于scanf("%d%d", &i, &j),scanf在读取键盘输入的时候,会读取第二个 %d 后面的空格,并存入缓冲区,这样缓冲区就会有残留的换行,我们对此验证
#include <stdio.h>
int main()
{
int i, j;
char ch;
scanf("%d%d", &i, &j);
printf("%d %d",i, j);
ch = getchar();
putchar(ch);
return 0;
}
打印结果:
第二个 %d 后面的换行存缓冲区,此时需要情况缓冲区,以防止对后面输入造成不需要的麻烦
优化代码如下
#include <stdio.h>
int main()
{
int i, j;
char ch;
scanf("%d%d", &i, &j);
printf("%d %d",i, j);
while('\n' != (ch = getchar()));
return 0;
}
运行结果:
已经清空缓冲区后面中的空白字符
以上就是我对scanf的认识了~~~