C语言的语法基础
4.scanf函数
scanf是格式化输入函数,可以从标准输入设备(通常指定为键盘)上,以各种不同的格式读入数据到变量。scanf函数的格式为:
scanf(格式化字符串, 地址表列);
其中格式化字符串包括以下三类不同的字符:
(1)输入格式说明,“输入格式说明”与printf函数中的“输出格式说明”基本相同;
(2)空白字符,指空格键、回车键(Enter键)和制表键(Tab键);
(3) 非空白字符,C语言的字符常量或字符串常量。
地址表列是若干个需要读入数据的地址项。各个地址项之间用“,”分开。地址项必须是C语言中合法的地址表达式,通常是变量的地址。此处的地址项称为输入项。例如有如下程序段:
int i;
float f;
scanf( "%d%f", &i, &f );
输入格式化字符串, 地址表列(输入项表列)
printf("i=%d,f=%8.3f\n", i, f);
上述程序段运行后从键盘输入:123 456.789回车
运行结果:
i=123,f= 456.789
此处“&”是“地址运算符”,&i表示变量i在内存中的地址。scanf函数先遇到输入格式说明"%d"先读入一个整型数123,然后遇到输入格式说明"%f",略去输入中的一个或多个连续空白字符后,读入一个实型数456.789,读入的数据存入相应的内存地址中,如图2.5所示:
遇格式说明"%d"读入一个整型数据123,存入变量i的地址处,i的值就是123
scanf ("%d%f", &i, &f);
遇格式说明"%f",略去输入中的一个或多个连续空白字符后,读入一个
实型数456.789存入变量f的地址处,f的值就是456.789
图2.5 scanf函数的输入格式说明与输入项的对应关系
如果输入的第一个数据前面有空白字符,则也要先略去这些空白字符,再开始读入第一个数据。例如上述程序段运行后从键盘输入:回车回车 123 456.789回车
屏幕上输出结果不变:
i=123,f= 456.789
5.调用scanf函数时应注意:
① 格式化字符串中,格式说明的类型必须与地址表列中输入项的类型由左至右一一对应匹配。如果类型不匹配,将不能读入正确数据。例如:
int i;
float f;
scanf("%d%d", &i, &f);
此处变量i可以正确读入数据,而变量f得不到正确数据。第二个格式说明"%d"要求与之对应的输入项是整型变量的地址,但&f为实型数据的地址与"%d"不匹配,因此出错;
② 当输入的数据少于输入项时,程序等待输入,直到所有输入项都读入数据为止,若有非法字符读入,scanf函数也结束输入。如下程序段
int i, j, k;
scanf("%d%d%d", &i, &j, &k);
当程序运行到上述scanf语句时,从键盘输入:
123 456 回车
此时把123赋值给变量i,把456赋值给变量j,由于还有一个输入项没有读入数据,程序等待输入, 此时若再从键盘输入:
789 回车
把789赋值给变量k,scanf函数结束输入。
如果输入的数据多于输入项时,多余的数据存储在输入缓冲区,留作下一个输入项的输入数据。系统为标准输入设备(通常指定为键盘)在内存开辟了一块区域作为输入缓冲区。由键盘输入的数据先存储在输入缓冲区中,程序再从输入缓冲区读取数据,没取完的数据就暂存于输入缓冲区中,等待程序取走;
③ 格式化字符串中,格式说明的个数应该与输入项的个数相同。
若格式说明的个数少于输入项的个数时,scanf函数结束输入,多余的数据项没接收新的数据。例如下例程序段
int i=10, j=20, k=30;
scanf(“%d%d“, &i, &j, &k);
执行时由键盘输入:1 2 3回车
结果变量i的值为1,j的值为2,但&k没有与之对应的格式说明,输入的数据3将不会赋给变量k,变量k仍保持原来的值30。多余的输入数据3存储在输入缓冲区,留作下一个scanf函数输入项的输入数据。
若格式说明的个数多于输入项的个数时,scanf函数结束输入,每个多余的格式说明将滤掉输入缓冲区中的一个输入数据。例如有如下语句段
int i, j, k;
scanf("%d%d%d", &i, &j);
scanf("%d",&k);
执行时由键盘输入:10 20 30 40回车
结果把10赋给变量i, 20赋给变量j,第一个scanf函数中多了一个格式说明"%d"将输入的30滤掉,然后把40赋给变量k。系统将提示错误信息:Null pointer assignment(指定了空指针),说明使用方法不正确。要滤掉输入缓冲区中的一个输入数据,正确的方法是在%与格式字符之间加一个“*”号。例如把上面程序段的第一个scanf改成:
scanf("%d%d%*d", &i, &j);
"%*d"表示从输入缓冲区中读走一个数据后不赋给任何变量。这时再运行上述程序段读入数据时,30被跳过去,把40赋值给变量k。这样就不会出现错误信息了。
④ 从键盘输入数据时,输入的各数据之间用空白字符 (空格键、回车键Enter或制表键Tab)隔开。空白字符可以一个,也可以连续几个。
⑤ 从键盘输入数据时,按空格键或制表键后,左边的数据并没有被程序接收,需要最后按一下回车键,scanf函数才能接受从键盘输入的数据。
⑥如果在格式化字符串中插入有某个非空白字符,输入数据时应输入一个与该非空白字符相同的字符,形成一一对应。例如下面程序段:
int i=1, j=2, k=3;
scanf("%d,%d,%d", &i, &j, &k);
要求键盘输入的每个数据之间紧跟一个逗号,下面是正确的输入
123,456,789回车
以下也是正确的输入。
123, 456, 789回车
但是下面的输入不正确,
123 456 789回车
以下的输入也不正确:
123;456;789回车
因为在格式化字符中有逗号(","),就要求输入一个","与之对应,如果","这一字符没有找到,本句scanf函数就结束输入,后两种输入方法只能把123赋值给变量i,变量j,k的值不变,j的值仍为2,k的值仍为3。再如,执行下述语句时:
scanf("i=%d,j=%d,k=%d", &i, &j, &k)
正确的输入数据格式是:
i=123,j=456,k=789回车
⑦如果在格式化字符串中插入有若干空白字符(空格键、回车键和制表键),输入数据时只要输入一个(或多个连续空白字符)。例如有如下程序段:
int i,j;
scanf("%d%d", &i, &j);
输入时两个数据之间应输入一个(或多个)空白字符,以下各行的输入均正确,
123456回车
123回车456回车
123456回车
这三种输入都把123赋值给变量i,456赋值给变量j。
⑧ scanf函数的地址表列中的输入项是地址,不是变量名,因此普通变量前应加“&”地址操作符。但是对于字符串数组或字符串指针变量(见第四章和第五章内容),其变量名本身就是地址,不需要在它们前面加“&”地址操作符。
⑨ scanf函数在调用结束后将返回一个函数值,其值等于有得到值的输入项的个数。
6.scanf函数的输入格式说明
每个格式说明都必须用 "%"开头,以一个格式字符作为结束,在此之间根据需要可以插入“宽度说明”、长度修饰符“l”和“L”等。
(1)格式字符
格式字符用于规定输入不同的数据类型,格式字符和它们的作用如表 2.12所示。
表2.12 Turbo C 2.0 提供的输入格式字符及其作用
格式字符 |
作 用 |
d |
输入十进制整数 |
i |
输入十进制整数,以前导0开始的八进制整数或0x开始的十六进制整数 |
o |
输入八进制整数 (可以带前导0,可以不带前导0) |
x |
输入十六进制整数 (可以带前导0x或0X,可以不带前导0x或0X) |
u |
输入无符号十进制整数 |
c |
以字符形式输入单个字符 |
s |
输入字符串,遇第一个空白字符结束 |
f, e |
以小数形式或指数形式输入实型数据 |
(2)长度修饰符
长度修饰符加在%和格式字符之间,长整型数一定要加l。例如%ld表示输入一个十进制长整型数据项;双精度实型数据的输入必须加长度修饰符l。例如%lf、%le。长双精度实型数据的输入必须加长度修饰符L。例如%Lf、%Le。长度修饰符及其作用见表2.13
表2.13 Turbo C 2.0 提供的长度修饰符及其作用
长度修饰符 |
作 用 |
l |
格式字符是d,i,o,u,x,时,用于输入长整型数据(long int) |
l |
格式字符是f,e时,用于输入双精度实型数据(double) |
L |
格式字符是f,e时,用于输入长双精度实型数据(long double) |
(3)输入数据的宽度
scanf函数输入数据的实际宽度是由输入数据的结束标志决定的,因为在读入某数据项时,遇到结束标志则完成该数据项读入。结束标志有三种
① 空白字符:空格键、回车键或制表键(TAB);
② 宽度m: 格式字符前可用一个整数m指定输入数据所占宽度,此时输入数据的宽度不能大于m。在scanf函数中,不能指定实型数据小数位的宽度;
③ 非法字符:由于非法字符的存在,构成了不正确的C常量。例如在输入整数时输入123r5,此处字母r为非法字符。
表2.14 举例说明scanf函数的用法。以下的各例句中,i,j为整型变量(int),k为长整型变量(long),ch为字符型变量(char),f为单精度实型变量(float),d为双精度实型变量(double),ld为长双精度实型变量(long double)。
表2.14 举例说明scanf函数的用法
语句: scanf("%3d%8f%d", &i, &f, &j); |
键盘输入数据:123456.7891234回车
结果:i的值为123, f的值为456.7891, j的值为234
说明:遇到宽度3和8,数据项结束。语句: scanf("%ld%c%d", &k, &ch, &i);
键盘输入数据:123456.7890回车
结果:k的值为123456, ch的值为'.', i的值为7890
说明:遇到非法字符'.',数据项结束。语句: scanf("%ld%lf%Lf", &k, &d, &ld);
键盘输入数据:12345678 回车 回车1234567.1234回车 123.456e+789回车
结果:k的值为12345678, d的值为1234567.1234, ld的值为1.23456e+791
说明:遇到空白字符,数据项结束。由于k为长整型变量、d为双精度实型变量、ld为长双精度实型变量,此处长度修饰符l与L是必须的,不能省略,否则无法正确读入数据。
(4)关于格式说明%c
在scanf函数中的格式说明%c用于输入单个字符,这时从键盘输入的空白字符将作为有效字符输入。例如:
char c1, c2, c3;
scanf("%c%c%c", &c1, &c2, &c3);
如果键盘输入:ABC
字符'A' 赋值给变量c1,字符''赋值给变量c2,字符'B' 赋值给变量c3。使用格式说明%c时空白字符不作为数据间的间隔,因此''作为下一个字符赋值给变量c2。回车键和制表键遇%c也同样赋值给相应的字符变量。
(5)连续使用多个scanf函数
[例2.2]在程序中连续使用多个scanf函数时,应注意消除前一行输入的回车符。
#include "stdio.h"
main()
{ int i, j;
float x=0.0 , y=0.0;
char c1, c2;
scanf("%d%d", &i, &j);
scanf("x=%f y=%f", &x, &y);
scanf("%c%c", &c1, &c2);
printf("i=%d,j=%d\n", i,j);
printf("x=%6.2f,y=%6.2f\n", x, y);
printf("c1=%c,c2=%c\n", c1, c2);
}
程序运行时,要求从键盘把1赋给i,2赋给j,1.1赋给x,2.2赋给y,'a' 赋给c1,'b' 给c2。键盘输入:
12回车
x=1.1y=1.2回车
运行结果:
i=1,j=2
x= 0.00,y= 0.00
c1=
,c2=x
上述结果显然是不正确,这是因为第一行输入的“回车”被第二个scanf函数给接收,而与第二个scanf函数要求输入“x=...”不符,第二个scanf函数立即停止执行,转去执行第三个scanf函数,把第一行输入的“回车”赋值给c1,再把'x'赋值给c2。
解决方法一,在第二、第三个scanf的格式字符串前加一个“空格”以抵消上一行输入的“回车”,如下:
scanf("x=%fy=%f", &x, &y);
scanf("%c%c", &c1, &c2);
解决方法二,分别在第二、第三个scanf函数前加一条函数调用语句fflush(stdin);本语句的作用是清除当前标准输入缓冲区内存放的数据,这样无论上一行输入了什么内容都作废,下个scanf将接收键盘输入的新数据。如下:
fflush(stdin);
scanf("x=%fy=%f", &x, &y);
fflush(stdin);
scanf("%c%c", &c1, &c2);
按上述方法更改后,执行[例2.2]时,键盘输入:
12回车
x=1.1 y=1.2回车
ab回车
运行结果:
i=1,j=2
x= 1.10,y= 2.20
c1=a,c2=b
发表评论