关于scanf、getchar、getch、getche缓冲区分析——C语言
缓冲区
根据数据刷新的时机可以将缓冲区的类型分为:全缓冲、行缓冲、无缓冲
(注意:Windows下的输出设备没有缓冲区,意思是printf是无缓冲的,但是在Linux下printf就是行缓冲的,至于为什么Windows下printf是无缓冲的,后文会提到)
全缓冲:当缓冲区被填满以后才进行真正的输入输出操作
行缓冲:当在输入或者输出的过程中遇到换行符时,才执行真正的输入输出操作
无缓冲:没有缓冲区,立即进行输入输出
(图片来源:https://www.cnblogs.com/mydomain/p/9817320.html)
(缓冲区其实是一块内存空间,它用在硬件设备和用户程序之间,用来缓存数据,目的是让快速的 CPU 不必等待慢速的输入输出设备,同时减少操作硬件的次数)
为什么Windows下C语言的printf是无缓冲的?
C语言作为一个面向过程的语言,算不上高级语言也不能说是低级语言,C语言完成的工作大多都是偏底层开发工作(再底层一点可能就是汇编了),C语言常常用在嵌入式、单片机之类的小缓冲区的东西上,
嵌入式设备的缓冲区通常远小于PC,如果printf是行缓冲的,可能刚写进去一点东西,就可能被别的玩意儿盖掉了,所以嵌入式做开发带流的函数进行I/O的时候通常一次I/O马上就要fflush()
缓冲区的刷新遵循以下的规则
1、不管是行缓冲还是全缓冲,缓冲区满时会自动刷新
2、行缓冲遇到换行符\n
时会刷新
3、关闭文件时会刷新缓冲区
4、程序关闭时一般也会刷新缓冲区,这个是由标准库来保障的
5、使用特定的函数也可以手动刷新缓冲区
C语言标准规定,输入输出缓冲区要具有以下特征:
1、当且仅当输入输出不涉及交互设备时,它们才可以是全缓冲的
2、错误显示设备不能带有缓冲区
所谓交互设备,就是现代计算机上的显示器和键盘。C标准虽然规定它们不能是全缓冲的,但并没有规定它们到底是行缓冲还是无缓冲,这就导致不同的平台有不同的实现
输入设备
Windows、Linux、Mac OS 在实现时都给输入设备带上了行缓冲,所以 scanf()、getchar()、gets() 在每个平台下的表现都一致
Windows 下特有的 getche() 和 getch()函数,都不带缓冲区
输出设备
Windows 平台下,输出设备是不带缓冲区的
Linux 和 Mac OS 平台下,输出设备带有行缓冲区
scanf函数——行缓冲
这个可能是最常用到的输入函数了,scanf() 是从标准输入设备(键盘)读取数据,当遇到 scanf() 函数时,程序会先检查输入缓冲区中是否有数据,是带有行缓冲区的,如果没有,就等待用户输入,
用户从键盘输入的每个字符都会暂时保存到缓冲区,直到按下回车键,产生换行符('\n'),输入结束
需要注意的是:当输入的数据与存储该数据的变量类型不符合,scanf() 还会尝试忽略一些空白符,例如空格、制表符、换行符等
例如:
#include <stdio.h>
#include <Windows.h> int main()
{
int a = , b = ; scanf("%d%d", &a, &b);
printf("%d %d", a, b); return ;
}
如果输入1(空格)2,中间的空格会被自动忽略到,也就是上面所说的会尝试忽略掉一些空白符(像前面这样的数字(空格)数字输入格式,无论中间输入多少的空白符会被忽略)
scanf还有一个比较奇怪的特性:只有当控制字符串以格式控制符开头时,才会忽略换行符、空格、制表符等空白符,为了读者理解这句话的含义,下面上代码
情况一:不会忽略换行符
#include <stdio.h>
#include <Windows.h> int main()
{
int a = , b = ; scanf("%d", &a);
scanf("b=%d", &b);
printf("%d %d", a, b); return ;
}
当输入1回车时,回车符虽然是属于空白符,但是此时不能被忽略,不能被忽略的换行符('\n')和格式控制字符串%d又不匹配,所以只能读取失败了,b的值不变还是20
情况二:会忽略换行符
#include <stdio.h>
#include <Windows.h> int main()
{
int a = , b = ; scanf("%d", &a);
scanf("%d", &b);
printf("%d %d", a, b); return ;
}
此时控制字符串变成以格式控制字符串(%d)开头,此时输入1回车,第二个scanf("%d", &b)就会把回车符('\n')忽略掉,等待用户输入下一个数字(在输入1回车后无论输入多少个空格\制表符\回车都会被忽略掉)
下面来分析下scanf("%s")输入字符串时的特点
scanf使用格式控制字符串%s读入字符串时,遇到回车、空格、制表符结束输入,下面将举多个栗子来分析scanf输入字符串时的特点
例一:
#include <stdio.h>
#include <Windows.h> int main()
{
char ss[][]; scanf("%s%s", ss[], ss[]); printf("%s%s\n", ss[], ss[]); return ;
}
输入为:Hello(空格)World
从结果可以看出,中间的空格被忽略掉了,现在来分析下过程:
前半部分的 Hello(空格) 碰到空格时就结束第一次输入,将Hello存储在ss[0]这个字符串中(注意:此时空格符仍然存在于缓冲区中,并没有被读走)继续读取第二个字符串,
此时scanf会先瞧一瞧缓冲区中有没有东西,发现里面有一个空格符,忽略掉,继续读取World,发现后面有个换行符,结束第二次输入,将World存储在ss[1]这个字符串中,换行符仍然留在缓冲区
(这里的特点和输入单个字符串一致,即在输入第一个非空白符前,输入多少个空格符(空格\制表符\回车)都会被忽略掉)
getchar()——行缓冲
getchar()可以看作scanf("%c");的一个简化版本,它和scanf("%c")的特性相同,这里就不再赘述了
gets()函数——行缓冲
gets(ss)遇到回车结束输入,读取的换行符被转换为NULL值,做为字符数组的最后一个字符,来结束字符串(意思就是将'\n'变成了'\0'),gets()以回车作为字符串的终止符,同时将回车符从输入缓冲区读走,
但不作为字符串的一部分。而scanf()不读走回车符,回车符仍留在输入缓冲区中,除了输入结束标志的不同外,gets和scanf在读取字符串时还有一个很大的区别,gets在读入字符串时不会忽略掉空白符,
意思是如果有语句gets(ss[0]),如果只按一下回车,那么回车就被当作字符串读入ss[0]这个字符串中,而不像scanf那样在读入到第一个非空白符之前忽略掉输入的所有空白符
代码示例:
#include <stdio.h>
#include <Windows.h> int main()
{
char ss[][]; gets(ss[]);
gets(ss[]); printf("%s%s", ss[], ss[]); return ;
}
getch()函数——不带缓冲区(无缓冲)
函数功能:当有输入时,立即读取(不需要按回车),不显示在屏幕上(不带回显),需要注意的是这个函数并非标准函数,跨平台使用时需要考虑移植性(getch() 位于 conio.h 头文件中,而这个头文件是 Windows 特有的,
Linux 和 Mac OS 下没有包含该头文件。换句话说,getche() 并不是标准函数,默认只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用)
getche()函数——不带缓冲区(无缓冲)
函数功能:当有输入时,立即读取(不需要按回车),并显示在屏幕上(带回显),和getch()一样位于conio.h中,是非标准函数,认只能在 Windows 下使用,不能在 Linux 和 Mac OS 下使用
关于清空缓冲区的三种方法
在谈到清空缓冲区的方法时,可能很多读者都会想到fflush(stdin)来清空缓冲区,但是fflush(stdin)存在一个问题,就是C语言标准规定,当 fflush() 用于stdout时,必须要有清空输出缓冲区的作用
但是C语言标准并没有规定 fflush() 用于stdin时的作用,编译器的实现者可以自由决定,所以它的行为是未定义的
(fflush(stdin) 这种不标准的写法只适用于一部分编译器,通用性比较差)
方法一:使用getchar()来清空缓冲区
特点:getchar() 是带有缓冲区的,并且一切字符通吃,或者说一切字符都会读取,不会忽略
int c;
while((c = getchar()) != '\n' && c != EOF);
方法二:使用scanf()来清空缓冲区
scanf("%*[^\n]"); scanf("%*c");
上面的语句分成两部分来看,第一部分:scanf("%*[^\n]"),这个语句作用是读取缓冲区中回车符('\n')之前的所有字符,并丢弃,在遇到回车符('\n')时便停止读取(注意:此时缓冲区中还有一个回车符),
第二部分:scanf("%*c"),这个语句的作用是读取一个字符,并丢弃,这样就将缓冲区中仅剩的一个回车符也读走了,此时就已经清空了缓冲区
方法三:使用fflush(stdin)来清空缓冲区
fflush(stdin);
不太推荐使用这种方法(还是因为通用性的问题,建议使用前面两种方法,因为前两种方法在任何平台、任何编译器、任何情景下都有效)
关于scanf、getchar、getch、getche缓冲区分析——C语言的更多相关文章
- 区分getchar(),getch(),getche()三个函数:
区分getchar(),getch(),getche()三个函数: 第一行是手动输入的,第二行是printf输出的. getch()和getche()这两个函数使用时要包含conio.h头文件: ge ...
- getchar() getch() getche() gets() puts() scanf()的用法及区别
getchar() putchar(ch) scanf() 头文件stdio.h getch() getche() 头文件conio.h gets() puts() 头文件stdio.h ...
- getch(),getche(),getchar()的区别
先说基本区别. (1) getch()和getche()函数 这两个函数都是从键盘上读入一个字符.其调用格式为: getch(); getche(); 两者的区别是 ...
- getchar() 、 scanf() 、流与缓冲区
C中的缓冲区一直是debug的重灾区,今天在写一个命令行界面的时候又遇到了这个问题,所以来总结一波. 两函数的不同之处 scanf() 会把 stdinBuff 中的特定格式数据取出,非特定格式数据则 ...
- scanf()/getchar()和gets()深入分析
C/C++学习笔记1 - 深入了解scanf()/getchar()和gets()等函数 ---------------------------------------------------- | ...
- 深入了解scanf()/getchar()和gets()/cin等函数
转:http://www.cnblogs.com/FCWORLD/archive/2010/12/04/1896511.html 转:问题描述一:(分析scanf()和getchar()读取字符) s ...
- 深入了解scanf() getchar()和gets()等函数之间的区别
scanf(), getchar()等都是标准输入函数,一般人都会觉得这几个函数非常简单,没什么特殊的.但是有时候却就是因为使用这些函数除了问题,却找不出其中的原因.下面先看一个很简单的程序: 程序1 ...
- geotrellis使用(十)缓冲区分析以及多种类型要素栅格化
目录 前言 缓冲区分析 多种类型要素栅格化 总结 参考链接 一.前言 上两篇文章介绍了如何使用Geotrellis进行矢量数据栅格化以及栅格渲染,本文主要介绍栅格化过程中常用到的缓冲区分 ...
- geotrellis使用(十六)使用缓冲区分析的方式解决投影变换中边缘数据值计算的问题
Geotrellis系列文章链接地址http://www.cnblogs.com/shoufengwei/p/5619419.html 目录 前言 问题探索 采样说明 实现方案 总结 一.前言 ...
随机推荐
- 【Linux】ApacheBench(ab)压力测试工具
AB的简介 ab是apachebench命令的缩写. ab是apache自带的压力测试工具.ab非常实用,它不仅可以对apache服务器进行网站访问压力测试,也可以对或其它类型的服务器进行压力测试.比 ...
- canvas 实现时钟效果
var clock = document.getElementById('clock'); var cxt = clock.getContext('2d'); function drawClock() ...
- rails 新建user的phonenumber字段
1.新建字段 //rails g migration add_字段名_to_表名 字段名:字段类型 rails g migration add_title_to_contents title:stri ...
- 爬虫模块之selenium模块
一 模块的介绍 selenium模块最开始是一个自动化测试的工具,驱动浏览器完全模拟浏览器自动测试. from selenium import webdriver # 驱动浏览器 browser=we ...
- RAID : 独立磁盘冗余阵列(Redundant Array of Independent Disks)
RAID 分为不用的等级(RAID0 - RAID5),以满足不同的数据应用需求. RAID 是由多个独立的高性能磁盘驱动器组成的磁盘子系统,从而提供比单个磁盘更高的存储性能和数据冗余的技术. AID ...
- 2018.12.15 spoj Longest Common Substring II(后缀自动机)
传送门 后缀自动机基础题. 给出10个串求最长公共子串. 我们对其中一个建一个samsamsam,然后用剩下九个去更新范围即可. 代码: #include<bits/stdc++.h> # ...
- java常用设计模式九:桥接模式
一.概述 将抽象部分与它的实现部分分离,使它们都可以独立地变化.它是一种对象结构型模式.比如存在2个维度,第一个维度有一个抽象类A和对应的子类A1和A2:第二个维度有另一个接口B和对应的子类B1和B2 ...
- C++ 中 new 操作符内幕:new operator、operator new、placement new
一.new 操作符(new operator) 人们有时好像喜欢有益使C++语言的术语难以理解.比方说new操作符(new operator)和operator new的差别. 当你写这种代码: st ...
- c# DirectoryInfo 类和 FileInfo 类
1.DirectoryInfo 类 DirectoryInfo 类派生自 FileSystemInfo 类.它提供了各种用于创建.移动.浏览目录和子目录的方法.该类不能被继承. 2.FileInfo ...
- php Amome框架 层次设计备注
层次说明: 每一级中函数都是为而且只为 上(高)一层 的文件服务的 最底层: AmemoMySql 基础数据库函数:AmemoConfig 数据库信息配置文件 再高一层: 一个文件对应一个 ...