浅谈getchar()和putchar()

1.getchar()是标准I/O标准库里的库函数

原型是int getchar(void)-----------它没有参数,原因是因为它是从stdin标准输入流中读入一个字符的函数,已经有了默认的流参数stdin了。其返回值是一个整型数,是用来表示字符用的。

输入输出往往都是内存与外设之间的数据来往,但是,内存的运行效率比外设的运行效率那要大得多,比如,从内存写一个字符到磁盘上,内存很快地传出字符数据,供给磁盘存储,磁盘花了很多的时间来存储,在这段存储的时间里,我们宝贵的内存只能干等着磁盘做完存储操作来传递下一个字符,这样的做法是非常浪费内存的。所以计算机牛人们就设计出了缓冲的概念,来缓和内存与外设之间速度不匹配的问题。这意味着程序的输入输出已经不是跟外设直接打交道了,而是从一块被称为缓冲区的内存区域来回复制数据。就C程序而言,所有的IO操作只是简单地从程序移进移出字节,这种字节流即为流。

回到getchar()函数中来,我们以下面一个程序为例。

#include<stdio.h>  
void main()
{
    char ch1 = getchar();  //从输入流中读取一个字符  
    char ch2 = getchar(); //从缓冲区中读取第二个字符 
    printf("%c\n", ch1);
    printf("%c\n", ch2);
}


结果会显示什么呢?如果是几年前的我肯定会回答说:“当然从ch1是第一次输入的字符,ch2是第二次输入的字符呗!”真的是这样子吗?如果你实践一下马上就会出现问题:为啥控制台界面只给我一次输入的机会呢?

第一种情况:如果你的输入只有一个字符,那么肯定的,ch1的值便是这个字符,那么ch2呢?在这里别忘了,你输完前一个字符后是通过什么结束输入的?是的,是Enter,Enter肯定会说:“人家可也是个正正经经的字符啊,不要不把无字幕的字符不当字符啊!”是的,在这里是向缓冲区中输入了两个字符,而getchar()是做什么呢?前面提到了,程序可不是直接跟外设打交道的,而是跟缓冲区打交道的,所以,getchar()库函数是从缓冲区中读取字符而不是从键盘读取字符的,所以,你已经向缓冲区中输入了两个字符够上面程序中两个getchar()读的了。可以用下面的程序测试一下这种效果。

  1. #include<stdio.h>  
  2. void main()  
  3. {  
  4.     char ch1 = getchar();  //从缓冲区中读取第一个字符  
  5.     char ch2 = getchar();  //从缓冲区中读取第二个字符  
  6.     getchar();  //从缓冲区中读取第三个字符  
  7.     printf("%c\n",ch1);  
  8.     printf("%c\n",ch2);  

在上面的程序中,有三次读入,所以如果你的输入是一个字符和一个换行,那么第三个getchar()在读stdin的缓冲区的时候发现缓冲区是空的,会提示输入设备输入数据,这里便可以再次输入了。

如果在第一次输入的时候不是简单的输入一个字符和一个换行,而是输入了一大堆字符,那么,这些字符将全部送往stdin流的缓冲区中,后面的所有包括getchar()的将从stdin流中读入数据的库函数将从这个缓冲区中读入数据,直道缓冲区再次为空后,才提示输入设备准备输入数据。

一、getchar的两点总结:
1.getchar是以行为单位进行存取的。
当用getchar进行输入时,如果输入的第一个字符为有效字符(即输入是文件结束符EOF,Windows下为组合键Ctrl+Z, Unix/Linux下为组合键Ctrl+D),那么只有当最后一个输入字符为换行符'\n'(也可以说是EOF)时, getchar才会停止执行,整个程序将会往下执行。譬如下面程序段: 可以清空缓冲区

while((c = getchar()) != EOF){
    putchar(c);
}

执行程序,输入:abc,然后回车。则程序就会去执行puchar(c),然后输出abc,这个地方不要忘了,系统输出的还有一个回车。然后可以继续输入,再次遇到换行符的时候,程序又会把那一行的输入的字符输出在终端上。


对于getchar,肯定很多初学的朋友会问,getchar不是以字符为单位读取的吗?那么,既然我输入了第一个字符a,肯定满足while循环(c = getchar()) != EOF的条件阿,那么应该执行putchar(c)在终端输出一个字符a。不错,我在用getchar的时候也是一直这么想的,但是程序就偏偏不着样执 行,而是必需读到一个换行符或者文件结束符EOF才进行一次输出。

对这个问题的一个解释是,在大师编写C的时候,当时并没有所谓终端输入的概念,所有的输入实际上都是按照文件进行读取的,文件中一般都是以行为单位的。因 此,只有遇到换行符,那么程序会认为输入结束,然后采取执行程序的其他部分。同时,输入是按照文件的方式存取的,那么要结束一个文件的输入就需用到EOF (Enf Of File). 这也就是为什么getchar结束输入退出时要用EOF的原因。

2.getchar()的返回值一般情况下是字符,但也可能是负值,即返回EOF。

这里要强调的一点就是,getchar函数通常返回终端所输入的字符,这些字符系统中对应的ASCII值都是非负的。因此,很多时候,我们会写这样的两行代码:
char c;
c = getchar();

这样就很有可能出现问题。因为getchar函数除了返回终端输入的字符外,在遇到Ctrl+D(Linux下)即文件结束符EOF时,getchar ()的返回EOF,这个EOF在函数库里一般定义为-1。因此,在这种情况下,getchar函数返回一个负值,把一个负值赋给一个char型的变量是不 正确的。为了能够让所定义的变量能够包含getchar函数返回的所有可能的值,正确的定义方法如下(K&R C中特别提到了这个问题):
int c;
c = getchar();

二、EOF的两点总结(主要指普通终端中的EOF)
1.EOF作为文件结束符时的情况:

EOF虽然是文件结束符,但并不是在任何情况下输入Ctrl+D(Windows下Ctrl+Z)都能够实现文件结束的功能,只有在下列的条件下,才作为文件结束符。
(1)遇到getcahr函数执行时,要输入第一个字符时就直接输入Ctrl+D,就可以跳出getchar(),去执行程序的其他部分;
(2)在前面输入的字符为换行符时,接着输入Ctrl+D;
(3)在前面有字符输入且不为换行符时,要连着输入两次Ctrl+D,这时第二次输入的Ctrl+D起到文件结束符的功能,至于第一次的Ctrl+D的作用将在下面介绍。
其实,这三种情况都可以总结为只有在getchar()提示新的一次输入时,直接输入Ctrl+D才相当于文件结束符。

2.EOF作为行结束符时的情况,这时候输入Ctrl+D并不能结束getchar(),而只能引发getchar()提示下一轮的输入。

这种情况主要是在进行getchar()新的一行输入时,当输入了若干字符(不能包含换行符)之后,直接输入Ctrl+D,此时的Ctrl+D并不是文件 结束符,而只是相当于换行符的功能,即结束当前的输入。以上面的代码段为例,如果执行时输入abc,然后Ctrl+D,程序输出结果为:
abcabc

注意:第一组abc为从终端输入的,然后输入Ctrl+D,就输出第二组abc,同时光标停在第二组字符的c后面,然后可以进行新一次的输入。这时如果再次输入Ctrl+D,则起到了文件结束符的作用,结束getchar()。
如果输入abc之后,然后回车,输入换行符的话,则终端显示为:
abc //第一行,带回车
abc //第二行
//第三行

其中第一行为终端输入,第二行为终端输出,光标停在了第三行处,等待新一次的终端输入。
从这里也可以看出Ctrl+D和换行符分别作为行结束符时,输出的不同结果。
EOF的作用也可以总结为:当终端有字符输入时,Ctrl+D产生的EOF相当于结束本行的输入,将引起getchar()新一轮的输入;当终端没有字符 输入或者可以说当getchar()读取新的一次输入时,输入Ctrl+D,此时产生的EOF相当于文件结束符,程序将结束getchar()的执行。

顺便提一下输出库函数。输出库函数的效果也只是将程序中的内容输出到缓冲区中,然后再刷新的时候输出到外设上,如果在输出的时候输出输出流stdout缓冲区中已经有了数据,那么输出的是已经有的内容,而不一定是你输出的内容。这是由缓冲区刷新的时间控制的,刷新的操作是根据具体的编译器来决定的,一般调用fflush(stdout)来刷新一下stdout缓冲区。



评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值