突然觉得自己看了很多别人写的东西,学到很多,可惜以后每次都遇到问题忘了又得去网上一通乱找,还找不到自己当初看的写的较好一些东西资料,索性决定从现在起把自己每天获得的看到的一些东西和总结写到博客里面。
不多说了,接下来开始说主题,探讨下我刚刚收集的一些关于stdin其实是流的资料吧。
简单来说,<stdin>是一个专用的文件句柄。解释下句柄吧(句柄是操作系统在生成对象时分配给对象的唯一标识,句柄不同于指针。如果你得到一个对象的指针,那你就可以对此对象做一切操作!于是系统不给你指针,而是给用户一个加了限制的,用于跟踪对象的指针的标识——句柄!系统使用句柄对外提供服务就相对安全了,操作系统是通过API提供服务的,对于用户来说,句柄等同于对象指针,但实际上句柄和指针不是一回事!)
刚刚看了书,发现自己真是什么都不懂啊,长了很多姿势。(stdin)就是标准输入的意思。就是以终端(计算机)为对象;即从键盘输入数据,运行结果到显示器屏幕上(就叫标准输入输出);再来解释下流的概念(流这个概念也解释不通,各种说法都有,反正我就暂理解为数据传输的字节序列吧)实际上,在内存中为每个数据流开辟一个内存缓冲区,用来存放流中的数据。当你向显示器输出数据时比如用(cout<<),现将这些数据送到程序中的输出缓冲区保存,直到缓冲区满了或遇到endl,就将缓冲区的全部数据送到显示器显示出来。在输入时,从键盘输入的数据先放在键盘缓冲区中,当按回车键时,键盘缓冲区中的数据输入到程序的输入缓冲区里。形成cin流。然后用提取预算符提取数据送给程序的有关变量。总之流是与内存缓冲区相对应的,或者说,缓冲区中的数据就是流。
接下来举几个例子说明:
代码语言:javascript复制#include<stdio.h>
int main()
{
char a;
char b;
scanf("%c",&a);
scanf("%c",&b);
printf("%d %d",a,b);
}
当你在键盘按下q之后就会打印出结果:
代码语言:javascript复制113 10
出现这个原因就是scanf()函数(你要是换成getchar()结果也一样)是从输入流缓冲区里读取值的,而并非是从键盘(也就是终端)缓冲区里读取,当按下q,q先放在键盘缓冲区里,当按下回车后,q以及“n”进入到输入缓冲区里面,所以第一次q被取走后,第二次读入函数直接从缓冲区里把“n”取走了。所以在读取成功后,就不会再从终端(键盘)读取,要解决这个的办法就是在第二次读取之前,清空缓冲区的残留数据。
1,、使用fflush(stdin)或者rewind(stdio);都可以起到清空缓冲区的作用,这俩个函数都包含在stdio.h这个头文件中 补充:(具体讲rewind(stdin)是把文件指针回绕到文件起始处。fflush(stdin)是把文件输入缓冲区清0)
代码语言:javascript复制2、或者下面这种方法(经典):int c;
while((c = getchar()) != 'n' && c != EOF);
由代码知,不停地使用getchar()获取缓冲区中字符,直到 获取的 字符c是换行符’n’或者是文件结尾符EOF为止。这个方法可以完美清除输入缓冲区,并且具备可移植性。
修改后如下:
代码语言:javascript复制#include<stdio.h>
int main()
{
char a;
char b;
scanf("%c",&a);
fflush(stdin);
scanf("%c",&b);
printf("%c %c",a,b);
}
这时候你输入a回车,b回车;就能输出a b;这个结果。
声明下是在windows 平台下的vc 测试的程序。
哦,对了还有要提的几点就是关于缓冲区:
首先我们知道缓冲区的这个东西的存在就是为了提高计算机的运算速度的(具体原因就是计算机对缓冲区的操作快于对磁盘的操作)
要知道缓冲区的类型有三个:全缓冲,行缓冲和不带缓冲:
1)全缓冲
在这种情况下,当填满标准I/O缓存后才进行实际I/O操作,全缓冲的典型代表是对磁盘文件的读写。
2)行缓冲
在这种情况下,当在输入和输出中遇到换行符时,执行真正的I/O操作。这时,我们输入的字符先存放在缓冲区,等按下回车键时才进行实际的I/O操作。典型代表就是我在讲的(stdin)和标准输出(sdout)/
3)不带缓冲
也就是不进行缓冲,标准出错情况stderr是典型代表,这使得出错信息可以直接尽快地显示出来。
ANSI(C89)要求缓存具有下列特征:
1.当且仅当标准输入输出并不涉及交互设备时,它们才是全缓存的。
2.标准出错决不会是全缓存的。
但是,这并没告诉我们如果标准输入输出涉及交互设备时,它们是不带缓存的,还是行缓存的,以及标准输出的不带缓存的,还是行缓存的。(貌似现在的标准都是c99了吧)
大部分系统默认使用以下缓存类型:
1.标准出错是不带缓存的。
2.如果是涉及终端设备的流,则它们是行缓存的,否则是全缓存的。
说了这么多吧,由于ANSI C对stdin,stdout和stderr的缓存特征没有强行的规定,以至于不同的系统可能有不同的缓存特征。只能说目前主要的缓存特征是:stdin和stdout是行缓存;而stderr是无缓存的。
关于缓冲区的大小问题:
如果我们没有自己设置缓冲区的话,系统会默认为标准输入输出设置一个缓冲区,通常是4096个字节的大小。(这和计算机的分页机制有关,因为进程在计算机中分配内存使用的就是分页与分段机制,并且每个页的大小是4096个字节,因此通常情况下缓冲区的大小为4096个字节。)
最后一点就是关于缓冲区的刷新(就是清空):
下列情况会引起缓冲区的刷新:
1.缓冲区满时;
2.行缓冲区遇到回车时;
3.关闭文件;
4.使用特定函数刷新缓冲区。
这里还忘了提一点,在很多时候,特别是循环输入数据的时候,比如你要输入的是int型,而你不小心输入了一个字符型,这里就会出现死循环 就是所谓缓冲区堵塞的问题,:
代码语言:javascript复制<pre name="code" class="cpp">#include <iostream>
using namespace std;
int main (){
int A ;
do
{
cout<<"请输入一个整数"<<endl;
cin>>A;
cout<<A<<endl;
}
while (true);
return 0;
}
这个程序首先会提示你输入一个整数,然后等待你输入,如果你输入的是整数,程序会输出刚才输入的整数,并且再次提示你输入一个整数,然后等待输入。但是如果你输入的不是整数(小数,或者字符),假设cin函数最后一次得到的整数是2,那么现在会不断的输出“请输入一个整数n 2”,这是因为a被定义为整形,如果输入了字母后,则这个字母会遗留在“输入缓冲区”中,因为缓冲区中有数据,古而cin函数不会等待用户输入,直接就去缓冲区读取,可是缓冲区中的却是字母,这个字母再次被遗留在缓冲区中,如此反复,从而导致不断的输出“ 请输入一个整数n 2”
解决办法如下:
代码语言:javascript复制#include <iostream>
using namespace std;
int main (){
int A ;
do
{
cout<<"请输入A的值"<<endl;
cin.clear(); //清除错误标记
fflush(stdin); //刷新缓冲区
cin>>A;
cout<<A<<endl;
}
while (true);
return 0;
}
在读到非法字符后,输入流将处于出错状态,调用clear是用来清除cin函数留下的错误标记的,然后才能调用fflush()方法来清除缓冲区的数据,当然用fflush方法可能移植性不好,(据说在GCC3.2不支持),由于我是在VC6.0下的编译的程序,所以这样是支持的,另外c 有很多别的清除缓冲区的函数,像ignore函数也可以。
还有rewind():这个是把文件指针恢复到文件开头的地方,用在stdin上就是清除了键盘缓冲区了,还有在当手动输入ctrl z(就是EOF)的时候会出现问题,rewind(stdin)也是用来清除EOF标志的。