scanf()是C语言中的一个输入函数。与printf函数一样,都被声明在头文件stdio.h里,因此在使用scanf函数时要加上#include
<stdio.h>。(在有一些实现中,printf函数与scanf函数在使用时可以不使用预编译命令#include
<stdio.h>。)它是格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中。
函数原型
int scanf(const char * restrict format,...);
函数 scanf()
是从标准输入流stdin
(标准输入设备,一般指向键盘)中读内容的通用子程序,可以说明的格式读入多个字符,并保存在对应地址的变量中。
函数的第一个参数是格式字符串,它指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置。每一个指针要求非空,并且与字符串中的格式符一一顺次对应。
函数返回值
scanf
函数返回成功读入的数据项数,读入数据时遇到了“文件结束”则返回EOF
。
scanf("%d%d",&a,&b);
函数返回值为int
型。
如果a和b都被成功读入,那么scanf
的返回值就是2;
如果只有a被成功读入,返回值为1;
如果a和b都未被成功读入,返回值为0;
如果遇到错误或遇到end of file,返回值为EOF
。
end of file为Ctrl+z 或者Ctrl+d。
在HDUOJ第一题设计scanf
的循环输入以及EOF
结束
while (~scanf("%d%d", &a, &b))
{
}
while (scanf("%d%d", &a, &b) != EOF)
{
}
正确读入a或者b,返回值为1,取反后为-2
正确读入a和b,返回值为2,取反后为-3
遇到错误或者遇到end of file,返回值为EOF,取反后为0,结束循环
while循环非0为真,0为假
使用scanf注意点
- 对于字符串数组或字符串指针变量,由于数组名可以转换为数组和指针变量名本身就是地址,因此使用scanf()函数时,不需要在它们前面加上"&"操作符。
char s[20];
scanf("%s", s);
- scanf中要求给出变量地址,如给出变量名则会出错。
int n;
// scanf("%d", n); 错误
scanf("%d", &n);
- 在输入字符数据(%c)时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。
scanf("%c%c%c", &a, &b, &c);
// 输入
// d e f
// a = 'd', b = ' ', c = 'e'
- 如果格式控制串中有非格式字符则输入时也要输入该非格式字符。
scanf("%d,%d,%d", &a, &b, &c);
// 输入
// 3,4,5
// a = 3, b = 4, c = 5
- 在输入int数据(%d)时,会跳过输入缓冲区的空白字符,从第一个非空白字符开始记录。
- scanf读入数据,每按一次回车会将键盘缓冲区内容写入到输入缓冲区,换行符并留在输入缓冲区中。
问题
键盘缓冲区残余问题
#include <stdio.h>
int main()
{
int a;
char ch = '0';
while (ch != 'N')
{
scanf("%d", &a);
scanf("%c", &ch);
printf("a = %d, ch = %c\n", a, ch);
}
}
我们从键盘下按下1和回车,发现printf语句就已经输出了。原因是因为每按一次回车,就向键盘缓冲区输入一个字符
'\n'
,而此时把'\n'
”错误“地赋给了ch,从而导致ch是换行符。解决的方案是在输入字符之前加一个getchar()
,来吸收这个回车符。
空白字符过滤
scanf()的格式控制串可以使用空白字符或其它非空白字符,使用空白字符会使scanf()函数在读操作中略去输入中的零个或多个空白字符。
观察以上代码,发现输入1 2 3 4 5 6 7 8 9 10
,输出是空格字符。因为scanf("%c", &ch)
,会逐个字符的读取,而且不会跳过空白字符,所以输入10次,ch最后一次对应的是' '
字符。
但如果我们在scanf的格式控制字符串中加入了空白字符,则在读入的时候会跳过若干空白字符。
我们在格式控制字符串中加入空格字符,在输入的时候便会跳过若干空格、Tab、回车,来达到成功输入的目的。
所以我们在输入字符的时候,需要特别注意键盘缓冲区和输入缓冲区的内容。
输入类型与格式化字符串不匹配导致stdin流堵塞
在执行第一个scanf时,尝试当读入'b'
发现与%d不匹配,直接导致输入不匹配直接返回1,表示只将stdin流中的1读入到了变量a中。
进行第二个scanf时,尝试读入'b'
发现仍不匹配,scanf直接返回0。表示读入0个值。
清空缓存区
不推荐使用fflush(stdin)
,因为此函数仅适用于部分编译器(如VC6),但是并非所有编译器都要支持这个功能(如gcc3.2)。这是一个对C标准的扩充。该函数没有很好的可移植性。
可以自己编写flush函数,此函数可以和scanf函数一起使用,但使用%c输入时要注意,即此函数只能用于缓冲区非空的情况。
void flush()
{
char ch;
while ((c = getchar()) != '\n' && c != EOF) ;
}