了解缓冲区的作用后我们将焦点移到标准输入输出,标准输入输出只是一个概念,指通过缺省设备交流,标准输出设备称为,标准输入设备称为stdin,在硬件上标准输出设备通常指显示器,输入设备通常指键盘,在软件上通常借助于系统自带的控制台,也可以不使用系统控制台,例如IDE自带的控制台 。和stdin定义了标准输入输出缓冲区大小、起始地址、读写指针、有效数据等内容,在C中它们是同一个结构体中定义的不同的缓存,对于支持访问和stdin的编译器,可以直接输出它们的成员,如下:
#includeint main(){puts("stdout");printf("%p\n",stdout); //直接输出stdoutprintf("%p\n",stdout->_base); //缓冲区起始地址printf("%d\n",stdout->_bufsiz); //缓冲区大小printf("%d\n",stdout->_cnt); //有效数据字节数printf("%p\n",stdout->_ptr); //读写指针puts("stdin");printf("%p\n",stdin); //直接输出stdinprintf("%p\n",stdin->_base); //缓冲区起始地址printf("%d\n",stdin->_bufsiz); //缓冲区大小printf("%d\n",stdin->_cnt); //有效数据字节数printf("%p\n",stdin->_ptr); //读写指针}
结果为:
B68C0
4096
4050
文章插图
B68F3
stdin
4096
0
由起始地址可以看到,输入和输出是两个不同缓冲区,它们的大小都是4k字节,读写指针开始为0,随着读写进行不断移动 。对于stdin来说有效缓冲数据大小对我们测试代码来说非常重要,请记住这个_cnt属性 。由于硬件不同、需求不同,缓冲也分为两种运行机制,一种是全缓冲,即当缓冲区填满才保存,例如硬盘;一种是半缓冲,也叫行缓冲,典型的设备是键盘,当遇到空格或回车时读取全部或一部分,这导致不同的C函数运行方式不同,我们自己编写代码时也可以选择全缓冲和半缓冲 。
现在轮到我们讨论scanf()了,它是一个标准输入函数,并采用半缓冲机制,现在带着缓存的概念诠释它的运行规则如下:
如果调用scanf()之前键盘缓冲区中有内容,可能导致失败,需要在调用前清空缓存 。当输入回车时本身也把\n放入缓存中,如果全部匹配成功,这个’\n’很可能还留在缓存中;如果只匹配了一部分,未匹配成功的字符和’\n’都会留在缓存中;如果匹配失败,留在缓存中的字符会更多 。匹配字符串时遇到空格会认为结束,剩下的字符串会留在缓存中 。
缓存不空是最频繁的一个问题,我们可以选择调用scanf()前清空缓存和调用scanf()后清空缓存,我倾向于每次调用scanf()后清空缓存,这样后面无论通过scanf()还是其它输入函数都不会碰到问题 。如果能确定缓存中只剩下一个’\n’,那么运行完后调用()或者用在scanf()中添加一个不赋值的%c,例如scanf(“%d%c”, &a),这种形式即可顺便清除最后回车键入的换行符 。有些编译器为了安全起见不允许格式符没有赋值的变量,例如VS会给出警告 。如果调用了一个别人的写的函数,不确定留在缓存中的字符数量,就只能清空所有缓存了,标准库提供了一个清除缓存的函数(stdin),但并非所有操作系统都支持,这也是C代码移植头疼的问题,支持这个函数,其它操作系统未必,通过以下代码测试你的平台支持情况:
#includeint main(){scanf("%s");printf("%d\n",stdin->_cnt);fflush(stdin);printf("%d\n",stdin->_cnt);return 0;}
当我们输入abc efg后,scanf()遇到空格停止匹配字符串,空格后面字符都留在缓冲区中,通过stdin检测缓存中的字符个数来比较调用()前后的变化,如果有变化那么恭喜你可以直接使用()清理缓存,如果不能只能采用其它方法,下面代码是一个笨方法:
- 示例代码 手机C语言代码,C语言
- c语言符号深度理解和再认识
- 手把手入门C语言—C语言指针
- 007 【编程之路扫雷游戏】(C语言实现)
- 世上最杰出程序员,B 语言、Unix 之父嫌计算机发展太慢,让孩子学生物?
- 都21世纪了,还会有人不学C语言吗?
- 如何设置Google Chrome的界面显示语言
- 设定 Chrome 的语言和区域
- C语言之printf简介
- 【深入理解JVM 六】虚拟机字节码执行系统