第一部分是介绍cout的原理
第二部分是介绍插入运算符
第三部分是对语法的介绍
1首先是cout的原理(cin近似)
先来看一个小例子
cout << "What are you doing?"
这里双括号引起的是字符串
<<符号表示该语句把字符串发送给cout,理解成该符号指出了信息流动的路径
而cout到底是什么呢?
它是一个预定义的对象,知道如何显示字符串,数字和单个字符等
目前只需要知道如何使用它,而不需要知道这个特定实例的内部情况,这也体现了对象的长处,无需得知内部情况就可以直接使用
可以看出cout有一个简单的接口,如果string是一个字符串,内容就是What are you doing?,那上述程序也可以写为
cout << string
这里不需要像C一样指定输出类型
这里需要理解一下输出过程
输出是一个流,cout对象表示这个流,其对象属性包括插入运算符(<<),它可以将右侧信息插入流中
因此我们与其说程序显示了字符串,不如说它将一个字符串插入到了输出流
理解了输出流就更容易理解cin了
2.再看cout的printf()的区别(插入运算符<<的妙用)
输出字符串已经说明,现在说整数
int carrots=25; cout << carrots;
这里会输出25,而不是字符串carrots,也不是字符串25,这两个与25D都有天壤之别
因为cout很聪明——它可以识别数据类型,从而改动显示的字符,即将carrots变量转化成整形数25(二进制存储),随后转化成字符2和5
这源于C++的面向对象特性。实际上,C++插入运算符(<<)将根据其后的数据类型相应调整行为,这是一个运算符重载的例子,具体的日后再学
习惯了printf之后感觉cout难以控制
但了解了之后发现cout与 printf相比十分便捷
一是cout可以识别类型,不用像printf用%s/%d等进行选择输出,变量是什么它就输出什么类型
当然你也可以通过强制类型转换来指定输出类型
二是它可以拓展的,即可以重新定义插入运算符,使得cout能识别更多更新的类型
三是cout也可以做到printf类似于域宽修饰的细致控制,这里日后再学
3.接下来学习cout和cin的语法知识
3.1cin>>的用法
cin可以连续从键盘读取数据(其实是从缓冲区读取),
语法格式:
//a可以是整型、实型、字符型、字符串型 int a; float b; char str[8]; string buf; cin >> a; // 单个输入 cin >> a >> b ; // 多个不同类型输入 cin >> str; // 以数组首地址为对象的输入 buf = str; //数组首地址和字符串变量赋值 cout << buf << str << a << b; //进行多个变量的打印
注意事项
3.1.1读入的数据和所需数据类型要相同
由于插入运算符<<是会识别对象的,因此cin读入的数据类型不同会导致一些问题
先说结论:读入数据与变量不匹配可能会导致cin返回FALSE,尤其是将字符型赋给整型
下面举一个个特殊的例子来说明
#include <iostream> using namespace std; int main() { int a; char b; cin >> a >> b; cout << a << " " << int(b) << endl; }
正常情况如下
但如果我们给char数组一个4,它会字符理解为4,并且输出4的ASCII码
但是char变量毕竟能对应所有的字符(包括回车换行,当然cin无法输入回车换行,原因参见本文3.1.4),而如果给a一个字符会怎么样呢?
那么这个情况为何会发生呢?原因如下:
cin 解析了变量a的类型,a的变量类型是int,因此cin只有接受到数字输入才是正确的
当你输入整型数值时,cin返回TRUE;输入字母时,cin返回值是FALSE,因此a变量被赋值默认值0.
因此cout<< a当然会打印出0啦
并且b显示-52,也说明cin发生了未知的错误,所以这样做是十分危险的
3.1.2.cin对分隔符的处理
先介绍一下标准输入缓冲区:
当我们从键盘输入字符串的时候需要敲一下回车键才能够将这个字符串送入到缓冲区中,那么敲入的这个回车键(\r)会被转换为一个换行符\n,这个换行符\n也会被存储在cin的缓冲区中并且被当成一个字符来计算!比如我们在键盘上敲下了123456这个字符串,然后敲一下回车键(\r)将这个字符串送入了缓冲区中,那么此时缓冲区中的字节个数是7 ,而不是6。
再说结论,cin会略过第一个分隔符
还是先给出一个例子
#include <iostream> using namespace std; int main() { int a; string buf,buf2; cin >> a; cin >> buf >> buf2; cout << buf << buf2 <<endl; }
当cin>>从缓冲区中读取数据时,若缓冲区中第一个字符是空格、tab或换行这些分隔符时,cin>>会将其忽略并清除,继续读取下一个字符,若缓冲区为空,则继续等待。
即在运行面板,我不断地摁回车,cin会略过,直到给a读入第一个数据(当然这个数据不一定是整型。)
可以看到,这里的输入窗口,在每个输入前,都含有若干个回车换行,cin是一律不读入的,因此可以正常的读入a和buf,buf2
3.1.3缓冲区和cin
先说结论:cin在缓冲区数据充足时不会请求键盘键入
cin读取数据也是从缓冲区中获取数据,缓冲区为空时,cin的成员函数会阻塞等待数据的到来。
一旦缓冲区中有数据,就触发cin的成员函数去读取数据。
而这也意味着,如果缓冲区有足够的数据,cin就不需要键盘的键入了
因此下面给出一个例子
#include <iostream> using namespace std; int main() { int a; char buf,buf2; cin >> a; cin >> buf >> buf2; cout << buf << buf2 <<endl; }
这里的调试窗口如下
可以看到啊,对aaaaa的缓冲区,buf读入之后,buf2直接读入,并且输出,程序结束
不需要第二次输入数据了
3.1.4cin.getline()和cin.get()的用法
先说结论:cin.getline() 与 cin.get() 的相同点是,都不会以空格符作为结束标志。区别是,cin.getline() 不会将行结束符(如换行符)残留在输入缓冲区中。
当学习了cin之后,会发现它存在一定的局限性
比如我仅想读入含有空格符的字符串
但是因为cin会以空格符作为结束符,因此会阻断读入
例如这个例子
#include <iostream> using namespace std; int main() { int a; string buf,buf2; cin >> a; cin >> buf >> buf2; cout << buf << buf2 <<endl; }
可以发现,buf是what+\0,而buf2读入了are+\0,这两个字符串内很明显无法包含空格
怎么处理呢?
我们引入getline和get函数,getline不会以空格为结束符,get是读入单个字符
其函数原型分别为
/*cin.get() 函数有多种重载形式,分为四种格式:无参,一参数,二参数,三个参数。 其中 streamsize 在 VC++ 中被定义为 long long 型。 其常用的的函数原型如下:*/ int get(); istream& get(char& var); //读取单个字符 istream& get( char* s, streamsize n ); //指定个数的字符串读取 istream& get( char* s, streamsize n, char delim); //指定结束符和个数的字符串读取 /*另外,cin.get() 函数还有两个重载形式不怎么使用,就不详述了,函数原型如下:*/ istream& get (streambuf& sb); istream& get (streambuf& sb, char delim); /*cin.getline()函数作用:从标准输入设备键盘读取一串字符串,并以指定的结束符结束。 函数原型有两个:*/ istream& getline(char* s, streamsize count); //默认以换行符结束 istream& getline(char* s, streamsize count, char delim); //指定换行符
他们强强联手,组合起来就能够实现我们的需求
还是给出若干个例子,用以介绍函数的具体用法
#include <iostream> using namespace std; int main() { char a; char array[20] = { NULL }; cin.get(array, 20); cin.get(a); cout << array << " " << (int)a << endl; return 0; }
结果上可以看出,我们成功利用cin.get()函数读入了空格,但是有一些小问题
array成功读入了缓冲区中含空格的字符串的what are you doing?(值得庆祝)
但是回车之后程序直接结束,a显示值为10,这就引发了一点小问题
首先我们要知道,回车符的ASCII码是10,那么问题就显而易见了
第一个问题:cin.get()函数读取一行时,遇到换行符时结束读取,但是不对换行符进行处理,换行符仍然残留在输入缓冲区。
第二个问题:cin.get()函数不会像cin一样略过第一个空白字符,导致它直接读入了上一个残留的回车换行符,并打印出其ASCII码
这里也可以解决办法:cin来代替cin.get(),这样cin不就会略过分隔符了吗
的确如此,但是如果使用了cin,就无法给a输入换行符了,这不是拆了东墙补了西墙吗?
鉴于以上几个问题,引出了cin.getline()函数
改写程序如下:
#include <iostream> using namespace std; int main() { char a; char array[20] = { NULL }; cin.getline(array, 20);//原本的get改为getline cin.get(a); cout << buf << " " << (int)a << endl; return 0; }
再次执行就会发现成功达到我们的要求
3.1.5getline()函数和get()函数
在上述讨论中,还有一个问题
第三个问题:cin.get()的形参只能是C风格的字符串,即字符型数组,不能使用C++的string类型变量
这就引出了getline()和get()函数
使用这两个函数需要
函数原型为:
//因为 getline() 函数的参数使用了 string 字符串,所以声明在了<string>头文件中了。 //getline()函数的原型为: istream& getline (istream& is, string& str); // 默认以换行符\n分隔行 istream& getline (istream& is, string& str, char delim); //gets() 是 C 中的库函数,在头文件 <stdio.h> 申明 //由于该函数是 C 的库函数,所以不建议使用,既然是 C++ 程序,就尽量使用 C++ 的库函数吧。 //另外,由于 gets() 实现不够安全,容易出现缓冲越界,用 fgets() 和 getline() 替代更好。gets() 在C++11中极不推荐使用,并在 C++14 中丢弃了该方法。 char *gets(char *buffer);
getline() 利用 cin 可以从标准输入设备键盘读取一行,当遇到如下三种情况会结束读操作: (1)文件结束; (2)遇到行分隔符; (3)输入达到最大限度。
简单的看一个例子
#include <string> #include <iostream> using namespace std; int main() { char a; string buf; getline(cin,buf);//原本的cin.getline改为getline cin.get(a); cout << buf << " " << (int)a << endl; return 0; }
注意,getline() 遇到结束符时,会将结束符一并读入指定的 string 中,再将结束符替换为空字符。因此,进行从键盘读取一行字符时,建议使用 getline,较为安全。
cin.getline() 与 getline() 类似,但是因为 cin.getline() 的输出是char*,getline() 的输出是 string,所以 cin.getline() 属于 istream 流,而 getline() 属于 string 流,二者是不一样的函数。
———————————————— 版权声明:本文为CSDN博主「恋喵大鲤鱼」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。 原文链接:C++ cin 详解之终极无惑_恋喵大鲤鱼的博客-CSDN博客_cin
3.1.5其他cin函数
这里列举一些具有丰富功能的cin函数
cin.gcount() //统计读入的字符串个数
getline(cin,buf) //buf是读入的字符串变量
//形参还可以写为(cin,buf,'c'),表示遇到c字符就停止读取,即增加了一个结束符
cin.get(b) //b是字符型变量