购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

7.2 使用scanf的注意事项

7.2.1 参数的个数一定要对应

在前面介绍printf时说过,“输出控制符”和“输出参数”无论在“顺序上”还是在“个数上”一定要一一对应。这句话同样对scanf有效,即“输入控制符”和“输入参数”无论在“顺序上”还是在“个数上”一定要一一对应。比如:


# include <stdio.h>
int main(void)
{
    char ch;
    int i;
    scanf("%c%d", &ch); 
    printf("ch = %c, i = %d\n", ch, i);
    return 0;
}
/*VC++6.0中的输出结果是:
--------------------------------------
a 6
ch = a, i = -858993460
--------------------------------------
*/

这种错误是初学者经常犯的,由于粗心大意,少写一个参数。更严重的是,这种错误在编译的时候不会报错。printf也是一样,即使“输出参数”少写了也不会报错,但从程序的功能上讲这么写就是错的。所以在编程的时候一定要避免这种错误的发生。

程序中为什么i=–858993460?这个在前面讲过,当变量没有初始化的时候就会输出这个值。在后面会讲到scanf是缓冲输入的,也就是说从键盘输入的数据都会先存放在内存中的一个缓冲区。只有按回车键后scanf才会进入这个缓冲区和取数据,所取数据的个数取决于scanf中“输入参数”的个数。所以上述程序中scanf只有一个输入参数,因此按回车键后scanf只会取一个数据。所以变量ch有数据,而变量i没有数据,没有数据就是没有初始化,输出就是–858993460。

7.2.2 输入的数据类型一定要与所需要的数据类型一致

在printf中,“输出控制符”的类型可以与数据的类型不一致,如:


# include <stdio.h>
int main(void)
{
    int i = 97;
    printf("i = %c\n", i);
    return 0;
}
/*VC++6.0中的输出结果是:
--------------------------------------
i = a
--------------------------------------
*/

但是在scanf中,对于从键盘输入的数据的类型、scanf中“输入控制符”的类型、变量所定义的类型,这三个类型一定要一致,否则就是错的。虽然编译的时候不会报错,但从程序功能的角度讲就是错的,则无法实现我们需要的功能。比如:


# include <stdio.h>
int main(void)
{
    int i;
    scanf("%d", &i);
    printf("i = %d\n", i);
    return 0;
}
/*VC++6.0中的输出结果是:
--------------------------------------
a
i = -858993460
--------------------------------------
*/

输出–858993460表示变量未初始化。为什么输入a,变量i却显示未初始化呢?在scanf中,从键盘输入的一切数据,不管是数字、字母,还是空格、回车、Tab等字符,都会被当作数据存入缓冲区。存储的顺序是先输入的排前面,后输入的依次往后排。按回车键的时候scanf开始进入缓冲区取数据,从前往后依次取。

但scanf中%d只识别“十进制整数”。对%d而言,空格、回车、Tab键都是区分数据与数据的分隔符。当scanf进入缓冲区中取数据的时候,如果%d遇到空格、回车、Tab键,那么它并不取用,而是跳过继续往后取后面的数据,直到取到“十进制整数”为止。对于被跳过和取出的数据,系统会将它从缓冲区中释放掉。未被跳过或取出的数据,系统会将它一直放在缓冲区中,直到下一个scanf来获取。但是如果%d遇到字母,那么它不会跳过也不会取用,而是直接从缓冲区跳出。所以上面这个程序,虽然scanf进入缓冲区了,但用户输入的是字母a,所以它什么都没取到就出来了,而变量i没有值,即未初始化,所以输出就是–858993460。

但如果将%d换成%c,那么任何数据都会被当作一个字符,不管是数字还是空格、回车、Tab键它都会取回。不但如此,前面讲过,你从键盘输入123,这个不是数字123,而是字符'1'、字符'2'和字符'3',它们依次排列在缓冲区中。因为每个字符变量char只能放一个字符。所以输入“123”之后按回车,scanf开始进入缓冲区,按照次序,先取字符'1',如果还要取就再取字符'2',以此类推。如果都取完了还有scanf要取数据,那么用户就需要再输入。先写一个程序看一下:


# include <stdio.h>
int main(void)
{
    char i, j, k;
    scanf("%c%c%c", &i, &j, &k);
    printf("i = %c, j = %c, k = %c\n", i, j, k);
    return 0;
}
/*VC++6.0中的输出结果是:
--------------------------------------
123
i = 1, j = 2, k = 3
--------------------------------------
*/

从这个程序中我们看出,就单纯地输入123,不加任何空格,按回车键之后就同我们所讲的一样,分别将字符'1'、字符'2'和字符'3'赋给字符变量i、j和k。

但是需要提醒大家注意的是,在之前程序中,因为scanf是%d,所以a没有被取出来,还在缓冲区中。当遇到下一个scanf是%c时它就会被取出来。但是如果一直没有出现%c,那么这时就会出现一个问题:“scanf怎么取十进制整数?”即使使用%d,但是由于字符a“挡”在最前面,scanf进去先碰到的总是a,也就无法取到它后面的整数,所以必须先将a“弄走”。这就牵涉到“清空输入缓冲区”的概念,这个稍后再讲。

7.2.3 在使用scanf之前使用printf提示输入

大家想一想,前面写的scanf程序有没有不足的地方?程序写好之后,编译、链接、执行,然后弹出黑窗口,出现一个光标在那不停地闪。对于编写程序的人来说他知道要输入什么,但是对于用户而言,用户怎么知道是什么意思呢?所以之前的程序都缺少提示信息!因此在使用scanf之前,最好先用printf提示用户以什么样的方式输入,这样可以大大提高代码的质量。看看下面这个程序:


# include <stdio.h>
int main(void)
{
    int i, j;
    printf("请输入两个值,中间以空格分隔:");
    scanf("%d%d", &i, &j);
    printf("i = %d, j = %d\n", i, j);
    return 0;
}

这样在执行的时候,用户一看就知道是要输入两个值,然后中间用空格隔开。所以这样写就更人性化、智能化了。 T4hTVVifPIAphLWcq+qA2C4aU2pRORk2TwzhfIRCOQfKcNRGHESSwW9I6fLk/C12

点击中间区域
呼出菜单
上一章
目录
下一章
×