C语⾔中scanf详解
scanf 的功能⽤⼀句话来概括就是“通过键盘给程序中的变量赋值”。该函数的原型为:
# include<stdio.h>
int scanf(const char*format,...);
它有两种⽤法,或者说有两种格式。
1. scanf(“输⼊控制符”, 输⼊参数);
功能:将从键盘输⼊的字符转化为“输⼊控制符”所规定格式的数据,然后存⼊以输⼊参数的值为地址的变量中。
下⾯给⼤家举个例⼦:
#include<stdio.h>
int main(void)
{
int i;
i =10;
printf("i = %d\n", i);
return0;
}
我们前⾯都是像这样写的,即直接给变量 i 赋⼀个值。但是这样写功能⽐较弱,因为这个值就变成⼀个“死值”了,它只能是 10,不可能是其他值,除⾮在程序中修改。很多时候我们希望这个值不是由程序员在程序中指定的,⽽是在程序运⾏的过程中由⽤户从键盘输⼊的。⽤户输⼊多少,变量i就是多少,这样程序的功能就更加灵活了。
那么如何实现在程序运⾏的过程中由⽤户从键盘输出值呢?⽤ scanf 即可实现:
# include<stdio.h>
int main(void)
{
int i;
scanf("%d",&i);//&i 表⽰变量 i 的地址,&是取地址符
printf("i = %d\n", i);
return0;
}
“输⼊控制符”和“输出控制符”是⼀模⼀样的。⽐如⼀个整型数据,通过 printf 输出时⽤%d输出,通过 scanf 输⼊时同样是⽤%d。
要想将程序中的 scanf ⾏弄明⽩,⾸先要清楚的是:我们从键盘输⼊的全部都是字符。⽐如从键盘输⼊ 123,它表⽰的并不是数字 123,⽽是字符 ‘1’、字符 ‘2’ 和字符 ‘3’。这是为什么呢?
操作系统内核就是这样运作的。操作系统在接收键盘数据时都将它当成字符来接收的。这时就需要⽤“输⼊控制符”将它转化⼀下。%d的含义就是要将从键盘输⼊的这些合法的字符转化成⼀个⼗进制数字。经过 %d 转化完之后,字符 123 就是数字 123 了。
第⼆个要弄清楚的是:&是⼀个取地址运算符,&后⾯加变量名表⽰“该变量的地址”,所以&i就表⽰变量 i 的地址。&i⼜称为“取地址i”,就相当于将数据存⼊以变量 i 的地址为地址的变量中。
那么以变量 i 的地址为地址的变量是哪个变量呢?就是变量 i。所以程序中 scanf 的结果就把值 123 放到变量i中。
综上所述,scanf 语句的意思就是:从键盘上输⼊字符 123,然后%d将这三个字符转化成⼗进制数 123,最后通过“取地址 i”到变量i 的地址,再将数字 123 放到以变量 i 的地址为地址的变量中,即变量 i 中,所以最终的输出结果就是i=123。
注意,为什么不直接说“放到变量i中”?⽽是说“放到以变量 i 的地址为地址的变量中”?因为这么说虽然很绕⼝,但是能加强对 &i 的理解,这么说更能表达 &i 的本质和内涵。很多⼈在学习 scanf 的时候,经常将“变量 i”和“变量 i 的地址”混淆,从⽽思维开始混乱,等深刻了解 &i 的含义之后就可以不那么说了。
以上是 scanf 的最简单⽤法,也是最常⽤、最基本、最重要的⽤法。这样通过 scanf 就可以在程序运⾏的过程中由⽤户来指定变量 i 的值,这与在程序中赋值相⽐较功能更强⼤。
2) scanf(“输⼊控制符⾮输⼊控制符”, 输⼊参数);
这种⽤法⼏乎是不⽤的,也建议你们永远都不要⽤。但是经常有⼈问,为什么 printf 中可以有“⾮输出控制符”,⽽ scanf 中就不可以
有“⾮输⼊控制符”。事实上不是不可以有,⽽是没有必要!下⾯来看⼀个程序:
# include<stdio.h>
int main(void)
{
int i;
scanf("i = %d",&i);
printf("i = %d\n", i);
return0;
}
在 printf 中,所有的“⾮输出控制符”都要原样输出。同样,在 scanf 中,所有的“⾮输⼊控制符”都要原样输⼊。所以在输⼊的时候i=必须要原样输⼊。⽐如要从键盘给变量 i 赋值 123,那么必须要输⼊i=123才正确,少⼀个都不⾏,否则就是错误。
所以 scanf 中%d后⾯也没有必要加\n,因为在 scanf 中\n不起换⾏的作⽤。它不但什么作⽤都没有,你还要原样将它输⼊⼀遍。
所以在 scanf 的使⽤中⼀定要记住:双引号内永远都不要加“⾮输⼊控制符”。除了“输⼊控制符”之外,什么都不要加,否则就是⾃⿇烦。⽽且对于⽤户⽽⾔,肯定是输⼊越简单越好。
⼀次给多个变量赋值:
# include<stdio.h>
int main(void)
scanf输入整型数组{
int i, j;
scanf("%d%d",&i,&j);
printf("i = %d, j = %d\n", i, j);
return0;
}
⾸先,scanf 中双引号内除了“输⼊控制符”之外不要加任何“⾮输⼊控制符”。通过键盘给多个变量赋值与给⼀个变量赋值其实是⼀样的。⽐如给两个变量赋值就写两个 %d,然后“输⼊参数”中对应写上两个“取地址变量”;给三个变量赋值就写三个 %d,然后“输⼊参数”中对应写上三个“取地址变量”……
但是需要注意的是,虽然 scanf 中没有加任何“⾮输⼊控制符”,但是从键盘输⼊数据时,给多个变量赋的值之间⼀定要⽤空格、回车或者 Tab 键隔开,⽤以区分是给不同变量赋的值。⽽且空格、回车或 Tab 键的数量不限,只要有就⾏。⼀般都使⽤⼀个空格。
此外强调⼀点:当⽤ scanf 从键盘给多个变量赋值时,scanf 中双引号内多个“输⼊控制符”之间千万不要加逗号,。
有些⼈觉得在输⼊的时候可以⽤逗号分隔,所以就在“输⼊控制符”之间⽤逗号隔开。这样做从程序的⾓度确实是可以的,但是建议⼤家不要这样做。在实际编程中这种写法是绝对不允许的,原因有两个:
⾸先逗号要原样输⼊的,有⼏个就要输⼊⼏个,少⼀个或多⼀个都不⾏;
其次,也是最主要的原因就是输⼊法的问题,在 scanf 中是在英⽂输⼊法下写的逗号,那么输⼊的时候如果是中⽂输⼊法下的逗号那也是错的。所以⽤逗号很容易出错。
最后再次强调:scanf“输⼊参数”的取地址符&千万不要忘了。这是初学者经常犯的错误。⽽ printf 中的“输出参数”是不带取地址符的,不要混淆了。
使⽤scanf的注意事项
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);
return0;
}
在 VC++ 6.0 中的输出结果是:
a 6
ch = a, i = -858993460
这种错误是初学者经常犯的,由于粗⼼⼤意,少写⼀个参数。更严重的是,这种错误在编译的时候不会报错。printf 也是⼀样,即使“输出参数”少写了也不会报错,但从程序的功能上讲这么写就是错的。所以在编程的时候⼀定要避免这种错误的发⽣。
程序中为什么 i=–858993460?这个在《为什么要初始化变量》中讲过,当变量没有初始化的时候就会
输出这个值。
在后⾯会讲到 scanf 是缓冲输⼊的,也就是说从键盘输⼊的数据都会先存放在内存中的⼀个缓冲区。只有按回车键后 scanf 才会进⼊这个缓冲区和取数据,所取数据的个数取决于 scanf 中“输⼊参数”的个数。所以上述程序中 scanf 只有⼀个输⼊参数,因此按回车键后scanf 只会取⼀个数据。所以变量 ch 有数据,⽽变量 i 没有数据,没有数据就是没有初始化,输出就是 –858993460。
2) 输⼊的数据类型⼀定要与所需要的数据类型⼀致
在 printf 中,“输出控制符”的类型可以与数据的类型不⼀致,如:
# include<stdio.h>
int main(void)
{
int i =97;
printf("i = %c\n", i);
return0;
}
在 VC++6.0中的输出结果是:
i = a
但是在 scanf 中,对于从键盘输⼊的数据的类型、scanf 中“输⼊控制符”的类型、变量所定义的类型,这三个类型⼀定要⼀致,否则就是错的。虽然编译的时候不会报错,但从程序功能的⾓度讲就是错的,则⽆法实现我们需要的功能。⽐如:
# include<stdio.h>
int main(void)
{
int i;
scanf("%d",&i);
printf("i = %d\n", i);
return0;
}
在 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);
return0;
}
在 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“弄⾛”。这就牵涉到“清空输⼊缓冲区”的概念,这个稍后再讲。
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);
return0;
}
这样在执⾏的时候,⽤户⼀看就知道是要输⼊两个值,然后中间⽤空格隔开。所以这样写就更⼈性化、智能化了。
⼩结
scanf 的使⽤看似细节繁杂,但使⽤起来⾮常简单。就⽬前⽽⾔,只要掌握以下五点:
在 scanf 的“输⼊参数”中,变量前⾯的取地址符&不要忘记。
scanf 中双引号内,除了“输⼊控制符”外什么都不要写。
“输出控制符”和“输出参数”⽆论在“顺序上”还是在“个数上”⼀定要⼀⼀对应。
“输⼊控制符”的类型和变量所定义的类型⼀定要⼀致。对于从键盘输⼊的数据的类型,数据是⽤户输⼊的,程序员是⽆法决定的,所以在写程序时要考虑容错处理,这个稍后再讲。
使⽤ scanf 之前先⽤ printf 提⽰输⼊。
只要掌握了以上五点,scanf 的使⽤基本上就没什么问题了。⾄于其他注意点,到后⾯讲数组和指针的时候再学习不迟。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论