scanf()函数
是通用终端格式化输入函数,它从标准输入设备(键盘) 读取输入的信息。可以读入任何固有类型的数据并自动把数值变换成适当的机内格式。
其调用格式为: scanf(“<格式化字符串>”,<地址表>);
scanf()函数接收输入数据时,遇以下情况结束一个数据的输入:(不是结束该scanf函数, scanf函数仅在每一个数据域均有数据,并按回车后结束)。
① 遇空格、“回车”、“跳格”键。
② 遇宽度结束。
③ 遇非法输入。
问题一
如何让scanf()函数正确接受有空格的字符串?如: I love you!
#include <stdio.h>
int main()
{
char arr[80];
scanf("%s",arr);
printf("%s",arr);
return 0;
}
输入:I love you
输出:I
上述程序并不能达到预期目的。因为scanf扫描到”I”后面的空格就认为对str的扫描结束(空格没有被扫描),并忽略后面的” love you”。
原因:残留的信息 love you是存在于stdin流中,而不是在键盘缓冲区中。
改进:
#include<stdio.h>
int main(void)
{
char arr[50];
scanf("%[^\n]",arr);//不能接收空格符
printf("%s\n",arr);
return 0;
}
问题二
键盘缓冲区残余信息问题
#include<stdio.h>
int main(void)
{
int a;
char c;
do{
scanf("%d",&a);
scanf("%c",&c);
printf("a=%d c=%c\n",a,c);
}while(c!='N');
return 0;
}
输入:1 4
输出:a=1 c=4
scanf(“%c”, &c);这句不能正常接收字符。我们每敲一下”Enter”键,向键盘缓冲区发去一个“回车”(\r),一个“换行”(\n),在这里\r被scanf()函数处理掉(执行拿数据),而\n被scanf()函数“错误”地赋给了c。
改进:
- 方案一
fflush(stdin)
while(ch=getchar() != ‘\n’)
{
;
}
#include<stdio.h>
int main(void)
{
int a;
char c;
do{
scanf("%d",&a);
fflush(stdin); //刷新输入缓冲区
scanf("%c",&c);
fflush(stdin);
printf("a=%d c=%c\n",a,c);
}while(c!='N');
return 0;
}
输入:1 4
输出:a=1 c=4
#include<stdio.h>
int main(void)
{
int a;
char c;
do{
scanf_s("%d", &a);
getchar();
scanf_s("%c", &c);
printf("a=%d c=%c\n", a, c);
} while (c != 'N');
return 0;
}
输入:1 4
输出:a=1 c=4

- 方案二
“空格符”来处理缓冲区残余信息的示例:
版本1:运行出错的程序
#include<stdio.h>
int main(void)
{
int i;
char j;
for(i=0;i<10;++i)
scanf("%c",&j);/*这里%前没有空格*/
printf("%c",j);/*在输入十个字符之后*/
return 0;
}
版本2:使用了空格控制符后
include<stdio.h>
int main(void)
{
int i;
char j;
for(i=0;i<10;++i)
scanf(" %c",&j);/*注意这里%前有个空格*/
printf("%c",j);
/*在输入十个字符之后, 验证打印出来的字符是否是自己
输入的最后一个字符(即输入的第十个字符)*/
return 0;
}
运行第一个版本(错误的程序)
我们输入:
0 1 2 3 4 5 6 7 8 9
结果是一个空字符
再运行第二个版本(正确的程序)
同样输入:
0 1 2 3 4 5 6 7 8 9
这一次就显示字符9,故此程序正确。
结论:就是%前面的空格在起作用,“scanf()的格式控制串可以使用空白字符或其它非空白字符,使用空白字符会使scanf()函数在读操作中略去输入中的零个或多个空白字符。”
所以在%前面加上了空格(空格属于空白字符,此外还有像制表符等也属于空白字符),在输入过程中,将略去输入中的一个或多个空白字符,所以我们输入的0 1 2 3 4 5 6 7 8 9这些字符中的空白字符就被略去了,字符9也就正确的打印出来了。
问题三
输入类型与格式化字符串不匹配导致stdin流的阻塞
#include<stdio.h>
int main(void)
{
int a=0,b=0,c=0,ret=0;
ret=scanf("%d%d%d",&a,&b,&c);
printf("第一次读入数量:%d\n",ret);
ret=scanf("%c%d%d",&a,&b,&c);
printf("第二次读入数量:%d\n",ret);
return 0;
}
输入:1 b 2
输出:第一次读入数量:1
输入:6
输出:第二次读入数量:3
执行到第一个scanf函数时,由于输入’b’的原因scanf函数直接返回1,stdin流阻塞。
执行到第二个scanf函数时,字符’b’与格式化字符串”%c%d%d”中的%c匹配,stdin流终于疏通,在输入6,则将变量a,b,c分别赋值为98(‘b’的ASCII码)、2、6,scanf函数返回3。
改进:
scanf函数后正确使用fflush(stdin);,清空输入缓冲区
#include<stdio.h>
int main(void)
{
int a=0,b=0,c=0,ret=0;
ret=scanf("%d%d%d",&a,&b,&c);
fflush(stdin);
printf("第一次读入数量:%d\n",ret);
ret=scanf("%d%d%d",&a,&b,&c);
fflush(stdin);
printf("第二次读入数量:%d\n",ret);
return 0;
}
输入:1 b 2
输出:第一次读入数量:1
输入:1 3 6
输出:第二次读入数量:3
问题四
如何处理scanf()函数误输入造成程序死锁或出错
#include<stdio.h>
int main(void)
{
int a,b,c;
scanf("%d,%d",&a,&b);
c=a+b;
printf("%d+%d=%d",a,b,c);
return 0;
}
如果正确输入a,b的值,那么没什么问题,但是,你不能保证使用者每一次都能正确输入,一旦输入了错误的类型,你的程序不是死锁,就是得到一个错误的结果
改进:
scanf()函数执行成功时的返回值是成功读取的变量数,也就是说,你这个scanf()函数有几个变量,如果scanf()函数全部正常读取,它就返回几。
但这里还要注意另一个问题,如果输入了非法数据,键盘缓冲区就可能还个有残余信息问题
#include<stdio.h>
int main(void)
{
int a,b,c;
while(scanf("%d%d",&a,&b)!=2)
fflush(stdin);
c=a+b;
printf("%d+%d=%d",a,b,c);
return 0;
}
补充:
fflush(stdin)这个方法在GCC下不可用。(在VC6.0下可以)
以下是 C99 对 fflush 函数的定义:
int fflush(FILE *stream);
如果stream指向输出流或者更新流(update stream),并且这个更新流
执行的操作不是输入,那么fflush函数将把任何未被写入的数据写入stream
指向的文件(如标准输出文件stdout)。否则,fflush函数的行为不确定。