大家好,这是我在CSDN的第一篇博客。我是一名学习GIS专业的大学生。我从小开始喜欢编程,可是到现在编程水平却长进不大,依然是菜鸟一个。究其原因,虽然这些年乱七八糟的东西学过不少,但是总的来说还是基础不够扎实。所以我从最近一段时间我便买了本C++ Primer Plus正式开始从零学习C++。我现在正准备出国留学平时主要时间用来学习英语,但每天还是会抽出40分钟看C++,看完一章便把书后的编程题挨个写一遍。现在我已经看到了第8章,也就是说与C有关的基础知识看完了。有一些心得体会想在这里整理一下,顺便与一起初学C++的同学们分享。这些体验不仅仅来自我自己的感悟,同样来自于CSDN论坛上各路高人的解答,在此要谢谢各位。

虽然说C++ Primer Plus的前7章差不多都是重述C语言的知识,但这并不代表你懂得点C语言就可以忽略这些内容,直接从OOP开始学!和输入和输出有关的缓冲区操作便是一个非常重要但又特别容易被我们这些C++菜鸟所忽视的内容,我在写C++ Primer Plus编程练习时出现的绝大多数bug都和它有关。

对于我这样从BASIC学到VB再学到C的人来说,想弄明白缓冲区是个什么东西还真不是一时半会的事,因为在BASIC和C中根本没有缓冲区的概念。好,现在先看一个小程序。

  1. #include<iostream>
  2. using namespace std ;
  3. int main()
  4. {
  5. char ch ;
  6. cin.get(ch);
  7. while (ch!='/n')
  8. {
  9. cout<<ch ;
  10. cin.get(ch);
  11. }
  12. return 0;
  13. }

在运行这个程序时,如果你输入123456789,回车后他将回显123456789。一开始我对此很费解,因为这种运行结果好像是输入一个字符串接着再输出它,但事实上并没用到字符串,这里便牵涉到了流的概念:传统的程序是输入设备给一个信息(这里理解为从键盘输入一个字符),程序就接收一个,而C++颠覆了这个概念,它使用了流和缓冲区。打个比方,程序是一个脸盆(囧~~~)用来接水的,水在这里比喻数据,传统的程序是直接往脸盆里倒水。而C++程序则在脸盆上加一个漏斗,漏斗上还有个塞子,水不是直接进脸盆而是先进漏斗,而且在进水时塞子是塞住的,当数据输入完毕时(回车时),塞子打开,数据才开始进入程序。从这个程序看,当运行到第一个cin.get时,因为缓冲区(漏斗)是空的,程序便停下来等待数据,输入一串字符并回车,注意这些字符没进入程序而是进入了缓冲区。然而第一个cin . get 只接收缓冲区前端的第一个字符,进入while循环后一切就变得很神奇。while循环中也有cin . get ,不过程序在这里却不会停了,因为缓冲区在这时候有数据,cin . get 便再从缓冲区读一个字符,这使得while循环在一瞬间就运行完成了,造成好像是在处理字符串的错觉。当然,这个程序演示的是把漏斗的水连续放空的情况,当然你也可以把塞子中途塞住。再看下面一个程序,这个程序更有助于理解缓冲区的工作原理:

  1. #include<iostream>
  2. using namespace std ;
  3. int main ()
  4. {
  5. char ch ;
  6. cin.get (ch);
  7. while (ch!='5')
  8. {
  9. cout<<ch ;
  10. cin.get(ch);
  11. }
  12. cout<<endl<<"下面还有哦"<<endl;
  13. while(ch!='/n')
  14. {
  15. cout<<ch;
  16. cin.get(ch);
  17. }
  18. return 0;
  19. }

程序设定在输入123456789后回显时接收到5的时候停止从缓冲区接收数据,输出一行中文字再接着回显,相当于在接收到5的时候把塞子塞住,打印一行字后再接着放。注意在中文字之前这个程序只回显1234,5在中文字后出现,各位可以想想为什么。

当然,缓冲区里有时候也会出现垃圾,设想一下下面一种情况。

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. char a[100];
  6. cin.getline(a,100,'@');
  7. cout<<a<<endl;
  8. char b[100];
  9. cin.getline(b,100,'@');
  10. cout<<b<<endl;
  11. return 0;
  12. }

这个程序先输入一个字符串,以@作结束符,回显这个字符串,再反复一次。这样运行程序显然没有问题:

I'm a C++ beginner.@

I'm a C++ beginner.

You're a VB beginner.@

You're a VB beginner.

可是这并不能说明程序就没有问题,一个完美程序不仅仅要在正确输入时有正确结果,更要在错误输入时不至于崩溃。这就是一个程序的坚固性,个人作为一个初学者认为初学者学习编程不能盲目图快图新,即便是再简单的程序也要具有优秀程序的完美特性。下面想象一下有些人天生比较猥琐故意想找你程序的茬,他可能会这么运行:

I'm a professional C++ coder.@HA HA HA!

I'm a professional C++ coder.

You're a C++ noob.@

HA HA HA!

You're a C++ noob.

这下子出问题了,第一个字符串中@后的内容明明不要了,却怎么还在第二个字符串回显时出现了,给人感觉极其诡异!实际上原因还是出在缓冲区上面,当getline方法扫描到@这个结束符时输入便结束了,剩下的内容(注意还包括你敲的那个回车)便留在了缓冲区中,第二个getline方法再执行时便先把刚才留在缓冲区中的那个HAHAHA一股脑放进了字符串b中,由于最后一个字符是回车不是@,所以输入并没有结束而缓冲区里却没东西了,于是程序出现了光标等待输入,给人一种从零开始的假象,实际上字符串b这时候已经有数据了,最后出现那种结果也不足为奇了。

想解决这个问题只有在输入字符串b之前把缓冲区清空,这里有一种比较容易想到的方式:

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. char a[100];
  6. cin.getline(a,100,'@');
  7. cout<<a;
  8. char waste;
  9. cin.get(waste);
  10. while(waste!='/n')
  11. cin.get(waste);
  12. char b[100];
  13. cin.getline(b,100,'@');
  14. cout<<b;
  15. return 0;
  16. }

这种方法就是把缓冲区里面不要的数据挨个扔掉,直到检测到回车符为止。可这种方法第一局限性很大,只能适应这个程序,第二就是时间复杂度高,遇到要抛弃大量数据的情况时耗时巨大。有没有一股脑把这些没用的数据倒掉的方法呢?有!请看下面:

  1. #include<iostream>
  2. using namespace std;
  3. int main()
  4. {
  5. char a[100];
  6. cin.getline(a,100,'@');
  7. cout<<a;
  8. cin.sync();
  9. char b[100];
  10. cin.getline(b,100,'@');
  11. cout<<b;
  12. return 0 ;
  13. }

这是我在CSDN里学到的方法,用cin的sync方法可以一下子把缓冲区清空,以上两个程序无论怎么输入便都可以正常执行了。

看来就算是C++里面向过程的东西都有这么多玄机在里面,所以说初学C++真的不要急于去研究OOP,基础打牢同样重要。鄙人也是个菜鸟,若有什么不对的地方请高手们指正啊!

cin.get()、流和缓冲区的更多相关文章

  1. getchar() 、 scanf() 、流与缓冲区

    C中的缓冲区一直是debug的重灾区,今天在写一个命令行界面的时候又遇到了这个问题,所以来总结一波. 两函数的不同之处 scanf() 会把 stdinBuff 中的特定格式数据取出,非特定格式数据则 ...

  2. JAVA之旅(二十五)——文件复制,字符流的缓冲区,BufferedWriter,BufferedReader,通过缓冲区复制文件,readLine工作原理,自定义readLine

    JAVA之旅(二十五)--文件复制,字符流的缓冲区,BufferedWriter,BufferedReader,通过缓冲区复制文件,readLine工作原理,自定义readLine 我们继续IO上个篇 ...

  3. Java精选笔记_IO流(字符输入输出流、字符文件输入输出流、字符流的缓冲区)

    字符流 Reader是字符输入流的基类,用于从某个源设备读取字符 Writer是字符输出流,用于向某个目标设备写入字符 字符流操作文件 字符输入流FileReader,通过此流可以从关联的文件中读取一 ...

  4. IO 流之字符流的缓冲区

    缓冲区的出现提高了对数据的读写效率 对应类: BufferedWriter BufferedReader 缓冲区需要结合流才可以使用, 对流的功能进行了增强, 即对流的操作起到装饰作用 使用缓冲区实现 ...

  5. DirectSound学习(二)--流式缓冲区

    使用流式缓冲方式播放波形音频文件比较复杂,主要原因是在只有一个缓冲区提供给用户的前提下,这个缓冲区在提供给声卡播放数据的同是还需要用户不断的定时向其中写入数据.要注意从缓冲区这时是一个环形缓冲区,声音 ...

  6. node学习笔记(二)流和缓冲区

    内容 视频 第四章内容 菜鸟教程服务器 //复制文件 function de(x) { console.log(x); } var fs=require('fs'); fs.mkdir('stuff' ...

  7. linux下清空c++ cin无效流的方式

    cin.clear(); cin.ignore(10000,'\n');//这里面的参数很重要

  8. cin关闭流同步加速

    习惯了用cin 很多人会说cin的速度比scanf慢很多, 其实不然. cin慢的原因主要在于默认cin与stdin总是保持同步, 这一步是消耗时间大户. 只需要加上std::iOS::sync_wi ...

  9. 黑马程序员——JAVA基础之IO流缓冲区,转换流,字节流

    ------- android培训.java培训.期待与您交流! ---------- 字符流的缓冲区        缓冲区的出现提高了对数据的读写效率. 对应类 •  BufferedWriter ...

随机推荐

  1. javaee Listener

    Servlet API 中定义了几种监听器,大多数尽管不是全部,都将监听某种形式的会话活动,通过实现对应事件的监听器接口订阅某个事件,然后在部署描述符中添加 <listener> 配置,或 ...

  2. Python3 Tkinter-Spinbox

    1.创建 from tkinter import * root=Tk() Spinbox(root).pack() root.mainloop() 2.参数 from_    最小值 to    最大 ...

  3. zabbix 2.2.2 安装部署

    zabbix 2.2.2版本与1.8.3版本安装过程略有不同,下面为实施步骤: 服务端:172.16.1.61 客户端:172.16.1.8 搭建zbbix软件 安装LAMP环境及依赖包 [root@ ...

  4. SGU 438 The Glorious Karlutka River =)(最大流)

    Description A group of Mtourists are walking along the Karlutka river. They want to cross the river, ...

  5. wpa_supplicant与kernel交互

    wpa_supplicant与kernel交互的操作,一般需要先明确驱动接口,以及用户态和kernel态的接口函数,以此来进行调用操作.这里分为4个步骤讨论. 1.首先需要明确指定的驱动接口.因为有较 ...

  6. # 团队作业MD

    队员姓名与学号 051501124 王彬(组长) 111500206 赵畅 031602215 胡展瑞 031602320 李恒达 031602131 佘岳昕 031602431 王源 0316022 ...

  7. c# 中base64字符串和图片的相互转换

    c#base64字符串转图片用到了bitmap类,封装 GDI+ 位图,此位图由图形图像及其特性的像素数据组成. Bitmap 是用于处理由像素数据定义的图像的对象. 具体bitmap类是什么可以自己 ...

  8. JS 书籍拓展内容

    一.面向对象

  9. Jenkins系列-Jenkins构建触发器

    触发器说明 build whenever a snapshot dependency is built,当job依赖的快照版本被build时,执行本job. 触发远程构建 (例如,使用脚本):这里使用 ...

  10. 自定义JS Map 函数

    // 自定义JS Map 函数 function Map() { var map = function (key, value) {//键值对 this.key = key; this.value = ...