C++中输入流的问题

这段时间在看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~

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值