缓冲区

计算机CPU的处理速度很快,而键盘的输入速度总是比不过CPU的处理速度,如果CPU一直等待键盘输入完,这样很浪费资源。为了解决速度的不匹配,引入缓冲区。

缓冲区就是内存里的一块区域,把数据先存内存里,然后一次性写入,类似于数据库的批量操作,这样大大提高高了数据的读写速率。

现在的外设控制器也有自己的缓冲池,如磁盘和网卡,通过缓冲IO的方式读取数据。

1 缓冲区的类型

缓冲区分为三种类型:全缓冲、行缓冲和不带缓冲。

  • 全缓冲
    只有当划定的缓冲区被填满或者数据读取至末尾时,才开始执行 I\O 操作(执行系统提供的 read\write 操作)。磁盘文件的读写一般采用这种方式。

  • 行缓冲
    当输入输出过程遇到换行符''\n"或者当分配缓冲区已满时,才开始执行 I\O 操作。一般涉及终端的读写操作如 stdin 与 stdout 使用这种缓冲方式。

  • 不带缓冲
    当有数据产生时,马上由相应的设备进行处理。一般来说 stderr(standard error) 使用这种缓冲方式,使得有错误信息时马上能够得到响应。需要注意的是,标准库不缓存并不意味着操作系统或者设备驱动不缓存。

2 缓冲区的刷新

缓冲区会在以下三种情况下被刷新:

  • 缓冲区满

  • 执行flush刷新缓冲区的语句

  • 程序正常结束。

3 行缓冲

标准输入缓冲区 stdin 使用行缓冲的方式存储输入。

用户的输入数据首先被暂存在临时缓冲区中,当用户键入回车键或临时缓冲区满后,stdin 才进行 I/O 操作,将数据由临时缓冲区拷贝至 stdin 中。

C语言提供的输入输出函数如 scanf 、getchar 等则从上述缓冲区 stdin 中读取数据输入。

4 getchar()

getchar()是stdio.h中的库函数,它的作用是从stdin流中读出第一个字符(包括空格、回车和Tab) 并清除,并不是直接从键盘这个硬件中读取输入的字符。

如果stdin有数据的话不用输入它就可以直接读取了,第一次getchar()时,确实需要人工的输入,但是如果输入多个字符,以后的getchar()再执行时就会直接从缓冲区中读取。

下面的语句可以清除回车:

while(getchar()!='\n');   

分析以下代码:

#include <stdio.h>
int main()
{
    char c;
    while((c = getchar()) != '\n')
        putchar(c);

    return 0;
}

程序运行过程:
getchar()会从输入缓冲区去读取内容。当所有的内容都输入完成并且按下了Enter键后,我们的输入才被送进去了输入缓冲区,这个时候,while循环才开始工作,每一次getchar()从输入缓冲区读取一个字符,然后如果不是换行符就输出。

在调用第一个getchar时输入了多个字符,那么,加入一个getchar并不能把所有未读取的字符过滤。如果希望重新从“键盘”读取的话,最好是加一个fflush(stdin):清除输入缓冲区

#include <stdio.h>
int main()
{
    char str[1000];
    char option='y';
    while(option!='n' && option!='N')
    {
        scanf( "%[^\n]", str );
        printf( "%s\n", str );
        printf("还要继续吗?");
        scanf("%c",&option);
    }
    return 0;
}

运行结果:


改进代码:

#include <stdio.h>
int main()
{
    char str[1000];
    char option='y';
    while(option!='n' && option!='N')
    {
        
        scanf( "%[^\n]", str );
        printf( "%s\n", str );
        fflush(stdin); 
        printf("还要继续吗?");
        scanf("%c",&option);
    }
}

运行结果:


#include <stdio.h>
int main()
{
    char str[1000];
    char option='y';
    while(option!='n' && option!='N')
    {
        fflush(stdin); 
        fgets(str, 1000, stdin);//尝试使用fgets
        printf( "%s\n", str );

        printf("还要继续吗?");
        scanf("%c",&option);
    }
}

运行结果:


image.png

5 scanf()

函数声明:int scanf( format string , arg1 , arg2 , ...);

从函数声明可以看到,scanf 的参数由指示读取动作的格式化字符串( format string )和相应的地址参数 arg1...argn 组成。scanf 函数将输入从标准输入缓冲区 stdin 中读入,并将它们以格式化字符串中指定的格式存储到额外的参数 arg1...arg2 等指定的内存空间中。其中额外的参数(additional argument)指向的内存空间的数据类型应该与格式化字符串中指定的数据类型相一致。

格式化字符串(format string)

格式化字符串规定了 scanf 等函数如何从输入缓冲 stdin 中读取数据,其组成字符的含义如下所示:

(1)空白字符(whitespace)。scanf 会读取并忽略在 stdin 中下一个非空白字符之前的所有空白字符(空格、换行和 tab),然后读取格式化字符串中规定格式的数据。若格式化字符串中包含空白字符,则该空白字符会与输入缓冲区中任意数量的连续空白字符相匹配,并将其从缓冲区中清除(包括0个)。例如格式化字符串"%d %d",会要求 scanf 首先从缓冲区中读取一个整型(若之前存在空白字符则跳过),再跳过输入缓冲区中连续的空白字符(与格式化字符串中的空白字符匹配),最后再读取一个整形;

(2)非空白字符(non whitespace)。对于格式化字符串中既非空白字符又不是格式说明符(format specifier,由%标识)的一部分的字符,scanf 会尝试从 stdin 中读取输入,并将输入与该字符比较,若匹配,则继续进行后续读取,若不匹配,则函数返回错误信息;

(3)格式说明符。以 % 开头的用于指定输入数据格式的字符。如 %d 指定需要读取一个整形,%s 需要读取一个字符串。scanf 等函数首先根据格式说明符尝试去解析 stdin 中的数据,如对于 %d ,scanf 会尝试对 stdin 中已有数据以整型的格式进行解析。若解析成功,则将上述解析结果存放到指定的内存中,若解析失败,如 stdin 中仅存在一个字符 'a',scanf 会退出并返回,但是上述不匹配的数据并不会从缓冲区中清除,后续的 scanf 调用仍从上述输入开始读取;

https://www.cnblogs.com/yhjoker/p/7530837.html

scanf异常处理

#include <stdio.h>
int main()
{
    int a, ret;
    ret = scanf("%d",&a);
    printf("%d%d\n",ret, a);
    while (ret != 1)
    {
        while (getchar() != '\n');
        ret = scanf("%d",&a);
        printf("%d%d\n",ret, a);
    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 212,657评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,662评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,143评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,732评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,837评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,036评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,126评论 3 410
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,868评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,315评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,641评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,773评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,470评论 4 333
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,126评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,859评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,095评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,584评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,676评论 2 351

推荐阅读更多精彩内容

  • 不知不觉,岁寒输入法的更新历史已经可以列出这么一长串来了。从中可以看出,岁寒的发展过程也是一个不断试错的过程,其中...
    临岁之寒阅读 33,928评论 1 6
  • 一、整洁代码 A.混乱的代价 1.有些团队在项目初期进展迅速,但有那么一两年的时间却慢去蜗行。对代码的每次修改都影...
    ZyBlog阅读 2,027评论 0 2
  • 肇庆,粤西的美丽小城,近两年在创文、广珠澳大湾区建设以及省运会承办地等项目的推动下,全市大兴土木。 因工程需要,火...
    白与灰_80阅读 607评论 0 0
  • 入秋后的清晨依旧惯性醒的很早,五点半,拉开窗帘瞅一眼,灰蒙蒙一片,一个下雨天。 “嗯,不能出门晨跑了。” 大脑下了...
    树漫漫阅读 1,597评论 10 4
  • 本来说好休假到月底,公司一通电话,像招魂术般把小月放飞自我的心招回到万恶的资本家手里。没办法,拿人钱财,替人卖命呗...
    liao一昂阅读 273评论 0 1