这段时间在看C++ Primer,发现了些关于输入流cin一些小细节的问题。
比如下面这段程序
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
cout << "Enter a string: ";
cin >> s;
cout << s << endl;
int v1,v2;
cout << "Enter two numbers: ";
cin >> v1 >> v2;
cout << "The sum of " << v1
<< " and " << v2
<< " is " << v1 + v2 << endl;
system("pause");
return 0;
}
程序很简单,就是先将用户输入的字符串输出出来,然后再把用户输入的两个整数的和输出出来。一个运行正常的输入和输出的例子为
输入hello,输出hello,输入2和3,输出5.
但是如果我们输入字符串的时候输入hello world...
See what happened!
在敲入了hello world按回车之后程序只输出hello并且后面没有让输入两个整数就直接输出了奇怪的结果。查了查,终于解决了一系列问号。
1. 为什么对输入“hello world”只输出了“hello"?
这个很简单,因为string读取字符时会忽略开头所有的空白字符,当再次遇到空白字符时,读取终止。所以程序读取到hello后面的空格时string对象s就认为读取完毕了,因此s的内容就是”hello“,进行输出。
2. 为什么后面没有输入两个整数就进行了输出?
这就涉及到输入流的缓冲区问题了。cin是istream类的对象,它从标准输入设备获取数据,程序中的变量通过流提取符”>>”从流中提取数据。当用户输入完数据按回车键时,该行所有的数据被送入输入流的缓冲区,供变量提取。因此当我们输入hello world时,它被整体送入缓冲区里,但如第一个问题里所讲,string对象只提取了hello。当程序运行到cin >> v1 >> v2时,两个整型变量到缓冲区里提取数据,此时缓冲区里是有数据的(即为剩下的"world"),因此程序不会等待用户键盘输入,而是直接提取缓冲区里的数据。如果缓冲区里的数据不够变量提取,则程序会等待用户新的输入到缓冲区里。下面的两个测试例子说明了这一机制。
初始时输入hello和2,hello被string对象读走并进行输出,剩下的2被v1读走,当v2想要提取数据时发现缓冲区为空了,因为等用户又输入了一个3的时候进行输出。
如果初始时输入hello 2 3 4 5,一按回车,这些全部都会被送入缓冲区,string对象和两个int型对象都顺利地读到了数据,因此不再需要输入就输出了。当然,缓冲区里数据的送入和提取遵循FIFO原则。
3. 为什么输入是“hello world”时后面两个整数自动变成了-858993460 ?
如前所述,string对象s提取走了hello,当v1和v2来缓冲区提取整数时,问题出现了,缓冲区里的头部位置的数据是字符串(或者更准确说v1和v2看到的应该是char型常量)。当遇到无效数字或者文件结束符时,输入流cin就处于出错状态,即无法正常提取数据。此时对cin流的所有提取操作都将终止。我们可以通过cin.fail()来查看输入流是否处于出错状态。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
cout << "Enter a string: ";
cin >> s;
cout << s << endl;
int v1,v2;
cout << "Enter two numbers: ";
cin >> v1 >> v2;
cout << "cin.fail(): " << cin.fail() << endl;
cout << "The sum of " << v1
<< " and " << v2
<< " is " << v1 + v2 << endl;
system("pause");
return 0;
}
cin.fail()返回1表示流处于错误状态。提取数据失败后,v1和v2没有被成功初始化,程序接着往后运行,在v1和v2进行输出和求和时被程序自动初始化为一个值。那为什么是统一的-858993460呢?因为这个结果是在debug下编译运行的,在release下未被初始化的局部变量就会被初始化为一个随机数。VC的debug版会把未初始化的指针和整型变量自动初始化为0xCCCCCCCC(也就是-858993460),而不是让它随机去,因为debug版的目的是为了方便我们调试程序,如果初值不确定,那么每次调试同一个程序就可能出现不一样的结果,源于同一个地方的错误,有可能有时程序崩掉有时不崩掉,这教人怎么debug......所以debug版本为了能让程序员更早地发现错误,就把未初始化的局部变量统一初始化为0xCC。常见的几种数据类型对应的值为:
int: -858993460 (0xCCCCCCCC)
unsigned, unsigned long: 3435973836 (0xCCCCCCCC)
short: -13108 (0xCCCC)
char: -52 '?' (0xCC)
double, long double: -9.2559631349317831e+061
bool: true
换到release版,就可以看到v1和v2被初始化为随机数了
另外,当提取失败时,输入流此后会一直处于错误状态,我们可以用cin.clear()操作将流重设为有效状态,但缓冲区里仍然是“world”,因此如果下一条语句还是cin >> v1 >> v2,则还是会发生提取失败,输入流会再次处于错误状态。我们可以用clear操作重设为有效状态后用cin.sync()操作清空输入流,这样后面cin时程序就会等待用户从键盘输入了。
#include <iostream>
#include <string>
using namespace std;
int main()
{
string s;
cout << "Enter a string: ";
cin >> s;
cout << s << endl;
int v1,v2;
cout << "Enter two numbers: \n";
cin >> v1 >> v2;
if(cin.fail()){
cout << "cin error occurs!" << endl;
cin.clear();
cin.sync();
cout << "Enter two numbers: \n";
cin >> v1 >> v2;
}
cout << "The sum of " << v1
<< " and " << v2
<< " is " << v1 + v2 << endl;
system("pause");
return 0;
}
此时再用hello world测试,结果为
写完了,回宿舍啦~O(∩_∩)O~