getchar、scanf以及缓冲区的概念
1、getchar()是stdio.h中的库函数,它的作用是从stdin流中读入一个字符,也就是说,如果stdin有数据的话不用输入它就可以直接读取了。
getch()和getche()是conio.h中的库函数,它的作用是从键盘接收字符,getchar带有回显。
与前面两个函数的区别在于: getchar()函数等待输入直到按回车才结束(前提是缓冲区没有数据),回车前的所有输入字符都会逐个显示在屏幕上。但只有第一个字符作为函数的返回值。
1 #include "stdio.h"
2 #include "stdlib.h"
3 int main(void)
4 {
5 char c;
6 while((c=getchar())!='\n') //每个getchar()依次读入一个字符
7 printf("%c",c); //按照原样输出
8 printf("\n");
9 system("pause");
10 return 0;
11 }
程序运行时,首先停下来,等你输入一串字符串,输入完毕后,它把你输入的整个字符串都输出来了,咦,你不是说getchar()只返回第一个字符么,这里怎么?
因为我们输入的字符串并不是取了第一个字符就把剩下的字符串丢掉了,它还在我们的内存中,好比,开闸放水,我们把水放到闸里去以后,开一次闸就放掉一点,开一次就放掉一点,直到放光了为止,这里开闸动作就相当于调用一次getchar()。我们输入的字符串也是这么一回事,首先我们输入的字符串是放在内存的缓冲区中的,我们调用一次getchar()就把缓冲区中里出口最近的一个字符输出,也就是最前面的一个字符输出,输出后,就把它释放掉了,但后面还有字符串,所以我们就用循环把最前面的一个字符一个个的在内存中释放掉,直到满足循环条件退出为止。
例子中循环条件里的'\n '实际上就是你输入字符串后的回车符,所以意思就是说,直到遇到回车符才结束循环,而getchar()函数就是等待输入(或缓冲区中的数据)直到按回车才结束,所以实现了整个字符串的输出。当然,我们也可以把循环条件改一下,比如while ((c=getchar())!='a'),什么意思呢,意思就是遇到字符'a'就停止循环,当然意思是如果你输入"12345a213123 "那么只会输出到a前面的那个字符,结果是12345。
再次注意:用getchar()它是从"流"中间去读取,所以第一个getchar()接受的是刚刚中断的流队列中即将出列的第一个字符(不限于回车符,上面举过例子了),如果流队列不为空,执行getchar()就继续放水,直到把回车符也放空为止,空了之后再在执行getchar()就停下等待你的输入了;我们用getch()为什么每次都是等待用户的输入呢?因为getch()是从键盘接收,即时的接收,并不是从stdin流中去读取数据。
下面是我的讨论:
先来一段code:
1 #include "stdio.h"
2 #include "stdlib.h"
3
4 int main(void)
5 {
6 char c;
7
8 for(;(c=getchar())!='a';)
9 printf("%c",c);
10 getchar();
11 c=getchar();
12 printf("%c",c);
13 system("pause");
14 return 0;
15 }
输入: ssss回车
得到: ssss
光标处(等待输入)
说明:由于一直没有输入字符a,所以此时程序没有结束,进入到for循环后一直没有退出。键入回车后,运行c=getchar(),依次从缓冲区内取出(for循环):'s''s''s''s'' ' 包括回车的换行符,并将其全部打印了出来。。
如果我们输入:ssssa回车
得到:ssss光标处(等待输入)
说明:程序读入到字符a的时候跳出了for循环,但是由于我们用getchar();清除了换行符'\n ',后面第7句c=getchar();需要你输入一个字符(因为ssssa后面并没有新的字符),所以程序仍然没有结束。如果我们注释掉getchar();这一句,那么c=getchar();这行代码就可以读取ssssa后面的回车符号了,就可以得到这样的输出:
ssss
光标处(程序结束)
这个输入ssssa中的回车中的换行符'\n '就被c=getchar();这一句读取并输出了。
总结:
键盘输入的字符都存到缓冲区内,一旦键入回车,getchar就进入缓冲区读取字符,一次只返回第一个字符作为getchar函数的值,如果有循环或足够多的getchar语句,就会依次读出缓冲区内的所有字符直到'\n '。要理解这一点,之所以你输入的一系列字符被依次读出来,是因为循环的作用使得反复利用getchar在缓冲区里读取字符,而不是getchar可以读取多个字符,事实上getchar每次只能读取一个字符。如果需要取消' \n'的影响,可以用getchar();来清除,这里getchar();只是取得了'\n '但是并没有赋给任何字符变量,所以不会有影响,相当于清除了这个字符,还要注意的是这里你在键盘上输入ssss看到的回显正是来自于getchar的作用,如果用getch就看不到你输入了什么。
2、scanf
scanf这个库函数比较奇怪,而且存在一定的缺陷,所以很多人都不用了,这里还是要简单介绍一下。
scanf输入字符串、整型、实型等数据判断的方式都一样,回车、空格、tab键都认为是一个数据的结束,当然字符的话,一个字符就是结束了。回车、空格等都有对应的ASCII码,所以用scanf输入字符时要小心这些东西被当成字符输进去,而输入字符串和整型、实型等数据时这些都被当成分隔符而不会被输入到字符数组或变量里。当然如果输入格式不是"%s%s"而是"%s,%s"分隔符就是逗号了。
说了这么多举几个例子:
1 #include "stdio.h"
2
3 int main(void)
4 {
5 char n1[10];
6 char n2[10];
7 scanf("%s",n1);
8 scanf("%s",n2);
9 printf("n1=%s,n2=%s",n1,n2);
10 return 0;
11 }
输入:
hello回车
world回车
得到如下的输出:
n1=hello,n2=wolrd光标处(程序结束)
这里hello后面就是输入再多个回车、空格也不会被赋值到n2中的,因为使用scanf函数输入字符串的时候他们只是被当做分隔符。另外就是输入n2的时候,n2后面的那个回车也被当做了分隔符,所以输出的时候,只是简单的输出了n1和n2的内容,而没有输出回车换行符。
如果输入:
hello回车
光标处(等待输入)
说明回车被认成分隔符,所以程序还要你输入一个字符串来赋给n2。
其实这时缓冲区里是有一个'\n '被留下来的,程序改成这样:
1 #include "stdio.h"
2
3 int main(void)
4 {
5 char n1[10];
6 char n2[10];
7 char n3,n4;
8 scanf("%s",n1);
9 scanf("%s",n2);
10 printf("n1=%s,n2=%s",n1,n2);
11 n3=getchar(); //n3读取了n2后面的那个回车字符并输出
12 printf("%c",n3);
13 //n4=getchar();
14 //printf("%c",n4);
15 return 0;
16 }
输入:
hello回车
world回车
得到:
n1=hello,n2=wolrd
光标处(程序结束)
如果取消最后两行的注释,同样的输入得到:
n1=hello,n2=wolrd
光标处(等待输入)
说明此时缓冲区内只有一个'\n ',第二个getchar就需要你再输入一个字符了,缓冲区内已经没有字符了。
scanf不会把回车、空格赋给字符串但是会赋给字符,就如同getchar一样,这时就要考虑'\n '的存在了。
比如:
1 #include "stdio.h"
2
3 int main(void)
4 {
5 char n1[10];
6 char n2;
7 scanf("%s",n1);
8 scanf("%c",&n2);
9 printf("n1=%s,n2=%d",n1,n2);
10 return 0;
11 }
输入:
hello回车
得到:
n1=hello,n2=10光标处(程序结束) //10是'\n '的ASCII码.
如果输入:
hello 空格回车(一定要有回车,因为scanf也是要等回车,准确说是' \n'才会去读缓冲区的。)
得到:
n1=hello,n2=32光标处(程序结束) //32是空格的ASCII码。
再罗嗦一下,如果最后一句输出n2=%d改成n2=%c,则输入:
hello回车
得到:
n1=hello,n2=
光标处(程序结束)
是不是和getchar一样可以把'\n '读出来呢,呵呵。
总结一下就是:
如果scanf输入的不是字符,那么分隔符为回车,空格、tab键时,两个数据之间的分隔符只是起区别两个数据的作用,把分隔好的两个数据分别赋值到各自定义好的变量或数组中去,两个数据之间的分隔符被从缓冲区读出但是不起任何作用,当然最后一个'\n '会被留在缓冲区内,除非用getchar();或scanf("%c",&c);把它读出来。
回车是一定要有的,不管getchar还是scanf只要是通过缓冲区输入数据的函数都是等待回车键'\n '出现才进入缓冲区的。再来个整型数据、字符串、字符的混合例子:
1 #include "stdio.h"
2
3 int main(void)
4 {
5 int a,b,c;
6 char n1[10];
7 char n2,n3;
8 scanf("%d%d",&a,&b);
9 scanf("%c",&n2);
10 scanf("%d",&c);
11 scanf("%s",n1);
12 scanf("%c",&n3);
13 printf("a=%d,b=%d,n2=%c,c=%d,n1=%s,n3=%c",a,b,n2,c,n1,n3);
14 return 0;
15 }
输入:
12(若干空格或回车都不影响结果,这里用了回车)
34(这里还要求输入,因为scanf只得到了一个整型数据,而缓冲区内没有整型数据了。要有回车或空格表示这个数据结束了,留下来的空格或回车被下个%c接受,这里用了回车,可以试一下空格)
45 jfdkjfa(回车)
得到:
a=12,b=34,n2=
,c=45,n1=jfdkjfa,n3=
光标处(程序结束)
这里说明一下过程:在前两个整型数据输入时,两个数据之间无论是回车还是若干空格都被scanf当做分隔符,好了,scanf读到分隔符(回车或空格)时,把第一个整型数据送到变量a中,缓冲区中留下分隔符和下面的整型数据,这时scanf再读当然先读分隔符,但是要求输入的还是整型数据(%d),所以分隔符被忽略,如果这时要求输入字符%c(不是字符串%s),那么分隔符将以一个字节的形式送到字符变量里,就如同这里的n2。同理可以知道c和n1的保存过程,最后的n3正是接收了输入时的最后一个回车。
好了如果看到这里你都理解了那么看最后一个例子:
1 #include "stdio.h"
2
3 int main(void)
4 {
5 int a;
6 char ch;
7 scanf("%d",&a);
8 ch=getchar();
9 printf("%d,%c",a,ch);
10 return 0;
11 }
输入:
95回车
得到:
95,
光标处(程序结束)
很明显这是由于分隔符(回车)被getchar读取并输出了,如果加入一句:getchar();
1 #include "stdio.h"
2
3 int main(void)
4 {
5 int a;
6 char ch;
7 scanf("%d",&a);
8 getchar();
9 ch=getchar();
10 printf("%d,%c",a,ch);
11 return 0;
12 }
输入:
95回车
c回车
得到:
95,c光标处(程序结束)