c语言的输入输出缓冲区

By | 2015年1月31日

在涉及到数据输入输出的时候,通常都会涉及到一个叫做缓冲区的东西。

其实他就是内存上的一块空间,用来临时存放数据。

为什么要这样做呢?当然是提高效率了。你想呀,当我们往磁盘上写一个文件时,一次写一个字符。如果直接往硬盘写入,1k个字符就需要写入一千次。

相对于cpu来说,硬盘是非常慢的,如果反复这样读写的话,会让cpu花费大量时间去等待磁盘完成读写工作。

如果我们专门使用一块内存,临时存放将要读/写的数据,效率就会提高不少。

比如我们用4kb的空间作为磁盘写入的缓存。每次写入的数据先存放在这块内存上,等到4kb内存都存满了,再将数据一次性写入硬盘内。如此一来,上面的那个程序只需要一次就能将所有需要写入的数据写入到硬盘上了。

使用键盘输入也是一样,通常我们数据的数据都是由多个字符组成的(比如一个浮点数),如果我们每输入一个字符就用去处理这些数据,只会吃力不讨好(因为有些数据我还没有输入呢,你处理了也得不到所需的信息)

缓冲区的分类

缓冲区有三种,分别是

  • 全缓冲
  • 行缓冲
  • 无缓冲

全缓冲就是直到缓冲区填满/读完,才会调用相关的IO函数进行处理。通常磁盘文件为全缓冲。

行缓冲是在遇到换行符(回车),或者缓冲区填满/读完时,就行相关的IO操作。通常键盘输入的数据都是行缓冲

无缓冲就是不带缓冲,输入输出的数据会立即访问相关设备,通常输出错误信息使用无缓冲。

缓冲区的刷新

我们可以调用某些函数来强制缓冲区刷新,在磁盘操作时,关闭一个文件也会造成缓冲区刷新。

 

考虑到本文的部分读者可能还没有磁盘文件操作的知识。故将行缓冲放在前面讲。

行缓冲

通常情况下,在控制台窗口显示文字,以及输入数据都使用行缓冲。

本来我们是可以展示这一点的,遗憾的是在Windows系统下,输出到控制台默认是不带缓冲的。。。

不过linux系统上是会有的。在此我们就不演示输出的行缓冲,而是输入的行缓冲

先让我们从这个程序开始

#include <stdio.h>

int main()
{
    putchar(getchar());
    putchar(getchar());
    return 0;
}

运行这个程序,正常情况下你会看到一个黑色的框,这符合我们的预期

2015-01-31 15 27 51

现在请输入两个符号,如图

2015-01-31 15 29 11

按照我们的程序,第五行的getchar会得到一个字符,然后将它返回,值作为putchar的参数,putchar会将它显示出来。可是现在我们很轻松的输入了两个字符。没错,就是缓冲区在作怪。我们输入的ab现在正躺在缓冲区内睡大觉呢!

因为输入是行缓冲,所以我们只需要按一下回车,系统就会调用getchar来处理数据!

按下回车后数据显示了出来

按下回车后数据显示了出来

咦,ab怎么一起显示出来了?!!!

原来在调用getchar后,只要缓冲区没有为空,那么接下来的getchar都不需要回车来触发。

算上回车,我们一共输入了三个字符,他们是ab和\n,

在按回车后,第一个getchar会从缓冲区内取出a字符,然后交给putchar显示出来。接着第二个getchar则会取出字符b,然后由putchar输出

现在,缓冲区内还应该存在着字符\n,怎么证明它呢,很简单!

再运行一次这个程序,这次我们只输入一个字符,然后按回车

2015-01-31 15 41 25

什么也没有发生啊。

请再仔细看,第二行a下面空出来了一行,而上面的运行结果中没有这一行。因为第二个getchar取出了换行符,然后显示了出来!

(如果你是从c语言教程数据输入那一节跳进来的,你只需要弄懂上面的部分就够了!现在是时候回到前面的学习进程中去了)

如果我们希望立即处理输入的数据呢,很简单咯,使用另一个函数。

准确说是两个函数,getch和getche,

这两个函数都是在我们一输入的时候就调用,用不着等你按下回车。唯一的区别是getch不会显示你输入的字符(无回显),getche则会将你输入的字符显示出来(有回显)。

#include <stdio.h>
#include <conio.h>      //getch函数在这个头文件中声明

int main()
{
    char data[11], tmp;
    int i = 0;
    while (i < 10)
    {
        tmp = getch();
        #ifdef _WIN32
        if (tmp == '\r')    //windows下换行符是\r\n
        #endif // WIN32

        #ifdef _LINUX
        if (tmp == '\n')    //linux下换行符是\n
        #endif // linux

        #ifdef _UNIX
        if (tmp == '\n')    //unix下换行符也是\n
        #endif // _unix

            break;
        data[i] = tmp;      //将tmp的数据保存到数组
        if ('\b' == tmp)    //如果输入的是退格符
        {
            if(i >= 0)      //只在有有效输入时往前退
            {
                i--;                //将字符串的索引退回一个
                printf("\b \b");    //退格,用空格将星号覆盖
            }

            continue;
        }
        putchar('*');       //输出星号
        i++;
    }
    data[i] = 0;            //字符串末尾添上'\0'
    printf("\nThe data you have inputted:%s\n", data);      //将数据输出
    return 0;
}

这里不对此程序做过多的介绍。

无缓冲

无缓冲没什么好说的,就是没有缓冲区啦。所有的信息都会第一时间刷新。

因此用在比较紧急的情况下。比如程序出错。

发表评论

电子邮件地址不会被公开。 必填项已用*标注