一、scanf函数介绍
二、空白字符和非空白字符
三、scanf函数使用注意事项
四、scanf函数使用常见问题
scanf()是C语言中的一个输入函数。与printf函数一样,都被声明在头文件stdio.h里,因此在使用scanf函数时要加上#include <stdio.h>。它是格式输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中。
1.函数原型:
int
scanf
(
const
char
* restrict format,...);
函数的第一个参数是格式字符串,它指定了输入的格式,并按照格式说明符解析输入对应位置的信息并存储于可变参数列表中对应的指针所指位置。每一个指针要求非空,并且与字符串中的格式符一一顺次对应。
函数 scanf() 是从标准输入流stdin (标准输入设备,一般指向键盘)中读内容的通用子程序,可以说明的格式读入多个字符,并保存在对应地址的变量中。
2.返回值:
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。
3.格式说明符
格式说明符 | 作用 |
c | 读入单个字符(后面不会加上空字节) |
s | 读入一个的字符序列,后面会加上空字节,遇到空白字符(\t \r \n 空格等)完成读取。 |
d | 读入可选有符号十进制整数 |
u | 读入无符号符号十进制整数 |
i | 读入可选有符号整数 |
a,e,f,g,A,E,F,G | 读入可选有符号浮点数 |
o | 读入可选有符号八进制整数 |
x,X | 读入可选有符号十六进制整数 |
p | 读入一个指针值 |
n | 不读入任何字符,而是把到该位置已读入的字符数存储到与之对应的int*指向的位置。 |
【注意】
空白字符
空白字符会使scanf函数在读操作中略去输入中的一个或多个空白字符。空白符可以是空格、制表符和新行符。
非空白字符
一个非空白字符会使scanf()函数在读入时剔除掉与这个非空白字符相同的字符。
【说明】:
(1)控制串中的空白符使 scanf() 在输入流中跳过一个或多个空白行。 本质上,控制串中的空白符使 scanf() 在输入流中读,但不保存结果,直到发现非空白字符为止。
(2)非空白符使 scanf() 在流中读一个匹配的字符并忽略之。例如,"%d,%d" 使 scanf() 先读入一个整数,读入中放弃逗号,然后读另一个整数。如未发现匹配,scanf() 返回。
(3)scanf() 中用于保存读入值的变元必须都是变量指针,即相应变量的地址。
(4)在输入流中,数据项必须由空格、制表符和新行符分割。逗号和分号等不是分隔符,比如以下代码:
scanf
(
"%d%d"
,&r,&c);
//将接受输入 10 20,但遇到 10,20 则失败。
(5)格式命令可以说明最大域宽。 在百分号(%)与格式码之间的整数用于限制从对应域读入的最大字符数。
scanf
(
"%20s"
,address);
//可以
向 address 读入不多于 20 个字符
如果输入流的内容多于 20 个字符,则下次 scanf() 从此次停止处开始读入。 若达到最大域宽前已遇到空白符,则对该域的读立即停止;此时,scanf() 跳到下一个域。
(6)虽然空格、制表符和新行符都用做域分割符号,但读单字符操作中却按一般字符处理。
scanf
(
"%c%c%c"
,&a,&b,&c);
//对输入流 "x y" 调用,返回后,x 在变量 a 中,空格在变量 b 中,y 在变量 c 中。
(7) scanf() 返回等于成功赋值的域数的值,但由于*而读入未赋值的域不计算在内。遇到文件结束则返回EOF;若出错则返回0。
(1)在高版本的 Visual Studio 编译器中,scanf 被认为是不安全的,被弃用,应当使用scanf_s代替 scanf。
(2) 对于字符串数组或字符串指针变量,由于数组名可以转换为数组和指针变量名本身就是地址,因此使用scanf()函数时,不需要在它们前面加上"&"操作符。
(3) scanf函数中没有类似printf的精度控制。
如: scanf("%5.2f",&a); 是非法的。不能企图用此语句输入小数为2位的实数。
(4) scanf中要求给出变量地址,如给出变量名则会出错
如 scanf("%d",a);是非法的,应改为scanf("%d",&a);才是合法的。
(5) 在输入多个数值数据时,若格式控制串中没有非格式字符作输入数据之间的间隔,则可用空格,制表符或回车作间隔。
C编译在碰到空格,制表符,回车或非法数据(如对“%d”输入“12A”时,A即为非法数据)时即认为该数据结束。
(6) 在输入字符数据(%c)时,若格式控制串中无非格式字符,则认为所有输入的字符均为有效字符。
实例演示:
#include
int main() {
char a,b;
scanf("%c%c", &a,&b);
printf("%c%c\n", a,b);
}
演示结果为:
结果分析:因为输入时把‘ ’(空格),赋给了b,所以打印的b是空格,而不是字符‘b’
(7) 如果格式控制串中有非格式字符则输入时也要输入该非格式字符。
#include
int main() {
char a,b;
scanf("%c,%c", &a,&b);
printf("%c%c\n", a,b);
}
演示结果:
结果分析:
输入的数据与输出的类型不一致时,虽然编译能够通过,但结果将不正确。
问题一:如何让scanf()函数正确接受有空格的字符串?
实例演示:
#include
int main() {
char a[20];
scanf("%s", &a);
printf("%s\n", a);
}
运行结果:
结果分析:上述程序并没有hello world。因为scanf扫描到"o"后面的空格就认为对str的扫描结束(空格没有被扫描),并忽略后面的"world"。残留的信息 "world"是存在于stdin流中,而不是在键盘缓冲区中。
解决方案:
#include
int main() {
char a[20];
scanf("%[^\n]", a);
printf("%s\n", a);
}
问题二:键盘缓冲区残余信息问题
实例演示:
#include
int main()
{
int a;
char c;
while (1)
{
scanf("%d", &a);
scanf("%c", &c);
printf("a=%d c=%c\n", a, c);
}
return 0;
}
运行结果:
结果分析:输入1后输入"Enter"键,即向键盘缓冲区发去一个“换行"(\n),在这里\n被scanf()函数处理掉了,“错误”地赋给了c.
解决方案:可以在两个scanf()函数之后加getchar()
问题三:输入类型与格式化字符不匹配导致stdin流的阻塞
#include
int main()
{
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;
return 0;
}
运行结果:
结果分析:由于输入错误键盘缓冲区就还有残余信息问题。
解决方案:每次输入后刷新缓冲区,使用fflush函数