解决scanf或者cin造成的死循环问题

本文探讨了使用scanf函数导致程序陷入死循环的问题及解决方案,包括使用fflush(stdin)、更改变量类型及采用getch函数等方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  最近有个同学拿了一个程序说让我帮忙给调试一下,我拿到看了一下发现,问题确实很怪异,他在写一个console程序并且希望能有一个简单的菜单,用户输入0-5之间的整数进行选择,输入错误会输出提示信息并要求重新输入,但是如果用户输入是一个字符的话,程序就会陷入死循环,不停地打印提示信息。

  问题明确了,答案也就出来了,显然是因为scanf函数或者cin读取输入字符时的问题,我用C写了小程序来演示这个问题,代码如下:

  

#include <stdio.h>

int main()
{
	int	number;

	printf("请输入0-5之间的数字:\n");
	for( ; ; )
	{
		scanf("%d",&number);
		if(number >= 0 && number <= 5)
		{
			printf("你输入的数字是%d。\n",number);
			break;
		}
		else
		{
			printf("输入有误,请重新输入!\n");
		}
	}
	return 0;
}

  造成死循环的原因也很简单,当第一次通过scanf函数读入整数时,如果我们输入的是一个字符,那么scanf将会什么都不读,直接返回0,输入错误提示信息后再次调用scanf读取时,由于我们上次输入的字符依旧在输入缓冲区里,所以scanf不需要等用户输入就直接返回0,然后第三次……第四次……这就是死循环的原因了。

  那怎么解决呢?也很简单,只要在for循环内部调用scanf函数前添加一句fflush(stdin)来强制刷新输入缓冲区就可以了,这样输入缓冲区里就又是空的了,所以scanf会等待用户输入。

  感谢键盘农夫在评论中指出了一个影响可移植性的问题:fflush(stdin)是未定义行为。查看了一下ISO9899.1999发现确实是这样的,从MSDN的介绍来看VC是完全支持这种做法的,其它编译器就不好说了,于是我找了另一个清空输入缓冲区的方法Flush the input buffer,但是这个方法在这里使用的话或许会影响代码可读性,可以自己创建一个FlushStdinBuffer函数完成这件工作。本段是May/06/2011新加内容。

  还可以怎么解决呢?我的同学提出了一个方法,就是将number不定义成int型,而是char型,那么到底行不行呢?乍一看上去似乎是可以的,但是如果用户输入是1287dst呢?程序将会只读第一个字符'1',然后认为这是合法的输入,这显然也是有问题的。

  那么不使用scanf函数,然后用getchar函数呢?还是有些问题,因为如果用户输入是一个字符的话,getchar将会读取两次,第一次读到的是对应字符的ASCII码,第二次读到的是那个换行符0x0A,所以错误提示信息也会出现两次。getc函数也会引起同样的结果,所以如果一定想用这两个函数的话,就要自己在for循环内部过滤掉对0x0A的判断。那么getch呢?这个函数显然是可以的,因为这个函数虽然也跟前面两个函数一样读输入,但是它不需要用户按回车就可以读取,所以只会读到用户输入的那一个字符,只是不回显罢了。

  显然,上面我们只提到了读取一个字符的函数,并没有提到gets之类的读取字符串的函数,因为这个小程序只允许输入字符,不过如果是其它程序需要这样做的话,gets是完全可以完成这个要求的,而且不会出现上面这么多问题,但是可一定不要忘记了缓冲区溢出哦。

  而C++用std::cin读取整型数字的时候也会出现scanf函数的问题,当输入不是整数时,它也是直接返回,下次就死循环了,所以在调用cin前可以先调用cin.clear()和cin.sync(),单独调用一个是没有用的哦,你或许会疑惑这是为什么呢?

  其实cin.clear()的作用并不像它的名字一样,当程序想要去读一个int型却读到了一个char型输入的时候,cin就会将自己的内部错误标识符设定为ios::failbit(没有错误是ios::goodbit),cin.clear()的作用不是清空输入缓冲区,而是清空这个内部错误标识符,真正清空输入缓冲区的是cin.sync(),但是只清空缓冲区也不行,因为内部错误标识符还保留着呢,下次读取的时候一看上次有错误,这次根本不读了……所以一定要一起调用。

  好了,关于这个问题就谈到这里了,如果你也有同样的问题依旧没有解决的话欢迎留言,我们一起讨论学习。

转载于:https://www.cnblogs.com/pianoid/archive/2011/05/03/2035656.html

<think>首先,用户的问题是:“我想了解C++cin的使用方法 请问C++中如何使用cin进行输入操作”。这是一个关于C++编程的问题,具体是关于cin输入流的用法。 根据系统级指令: - 所有行内数学表达式必须使用$...$格式,但这个问题是关于C++的,可能没有数学表达式,所以我可以忽略这个。 - 独立公式使用$$...$$,但同样,这里可能不需要。 - LaTeX语法正确:确保任何代码或数学表达正确。 - 使用中文回答。 - 生成相关问题:在回答结束时,生成相关问题。 - 回答中引用的段落末尾自然地添加引用标识,如[^1]。 - 回答结构清晰,帮助用户逐步解决问题,保证真实可靠。 用户提供了引用内容: - 引用[1]:关于cin输入非法字符的问题,导致死循环。 - 引用[2]:关于cin输入字符和空白符的处理,包括noskipws的使用。 这些引用是站内引用,我应该参考它们,但用户说“上述引用仅供参考,并不是用户真正的需求”,所以我需要基于用户的真正需求来回答:cin的使用方法。 用户的需求是了解cin的基本使用方法,包括如何输入各种数据类型、处理错误等。 结构化回答: 1. **介绍cin**:解释cin是什么,它是C++标准输入流,用于从键盘或其他输入源读取数据。 2. **基本语法**:展示cin的基本用法,如`cin >> variable;`。 3. **输入不同类型数据**:解释如何输入整数、浮点数、字符串、字符等。 4. **处理输入错误**:讨论当输入不匹配时的问题,如引用[1]提到的死循环问题,并提供解决方案。 5. **处理空白符**:如引用[2]提到的,使用noskipws来读取空白符。 6. **完整示例**:提供一些简单的代码示例来演示。 7. **最佳实践**:给出一些建议,避免常见错误。 8. **生成相关问题**:在结束时,生成相关问题。 确保回答用中文。 最终,生成相关问题,如“§§相关问题§§”部分。 相关问题应该基于当前主题,例如: 1. cin和getline的区别? 2. 如何清除输入缓冲区? 3. cin在文件输入中的应用? 4. 输入验证的最佳实践? 现在,构建回答: - 开头:直接回应问题。 - 主体:逐步解释。 - 结尾:相关问题。</think>### C++cin输入操作的使用方法 在C++编程中,`cin`(发音为“see-in”)是标准输入流对象,用于从键盘或其他输入源读取数据。它是`iostream`库的一部分,定义在`<iostream>`头文件中。`cin`的使用涉及基本语法、数据类型处理、错误处理以及一些高级技巧。下面我将逐步解释其核心用法,确保回答清晰可靠,并基于C++标准(如C++11及以上)提供示例。参考提供的引用内容,我会在相关部分添加引用标识[^1][^2]。 #### 1. **cin的基本语法和用法** `cin`通过`>>`操作符(提取操作符)读取输入数据,语法为`std::cin >> variable;`。其中`variable`是已声明的变量(如整数、浮点数、字符或字符串)。 - **输入整数或浮点数**:直接读取数值数据。 ```cpp #include <iostream> int main() { int num; std::cout << "Enter an integer: "; std::cin >> num; // 输入整数 std::cout << "You entered: " << num << std::endl; double value; std::cout << "Enter a floating-point number: "; std::cin >> value; // 输入浮点数 std::cout << "You entered: " << value << std::endl; return 0; } ``` 如果用户输入`42`和`3.14`,程序将正确输出这些值。但注意,如果输入非数值字符(如字母),`cin`会进入错误状态,导致后续输入失败[^1]。 - **输入字符和字符串**: - 字符:使用`char`变量读取单个字符(默认跳过空白符如空格、换行)。 ```cpp char ch; std::cout << "Enter a character: "; std::cin >> ch; // 跳过空白符,只读取非空白字符 std::cout << "You entered: " << ch << std::endl; ``` - 字符串:使用`std::string`(需包含`<string>`头文件)读取单词(以空白符分隔)。 ```cpp #include <string> std::string name; std::cout << "Enter your name: "; std::cin >> name; // 读取单个单词(空格前的内容) std::cout << "Hello, " << name << "!" << std::endl; ``` 如果输入`John Doe`,`cin`只读取`John`,因为空白符作为分隔符被忽略。 #### 2. **处理输入错误和非法字符** 当输入与变量类型不匹配时(如输入字母给整数变量),`cin`会失败: - **错误状态**:`cin`进入失败状态(`failbit`被设置),变量不被修改,输入流中的非法数据残留,导致后续操作死循环或意外行为[^1]。例如: ```cpp int i = 0; while (i != -1) { std::cin >> i; // 如果输入字母,i不变,循环无限执行 std::cout << "You entered " << i << '\n'; } ``` 输入`abc`后,程序会不断输出上一次的`i`值(如0),无法继续输入。 - **解决方案**:使用`cin.clear()`清除错误状态,并用`cin.ignore()`忽略残留字符。 ```cpp #include <iostream> #include <limits> int main() { int num; while (true) { std::cout << "Enter a number (or -1 to quit): "; std::cin >> num; if (std::cin.fail()) { // 检查输入失败 std::cin.clear(); // 清除错误状态 std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); // 忽略整行残留字符 std::cout << "Invalid input! Please try again." << std::endl; } else { if (num == -1) break; std::cout << "You entered: " << num << std::endl; } } return 0; } ``` 此代码能处理非法输入,避免死循环[^1]。 #### 3. **处理空白符和整行输入** 默认情况下,`cin >>`跳过空白符(空格、制表符、换行)。要读取空白符或整行文本: - **使用`noskipws`读取空白符**:如引用[2]所述,`noskipws`操纵符允许读取空白符。 ```cpp #include <iostream> int main() { char ch; std::cout << "Enter a character (including whitespace): "; std::cin >> std::noskipws >> ch; // 读取空白符 std::cout << "You entered ASCII: " << static_cast<int>(ch) << std::endl; std::cin >> std::skipws; // 恢复默认跳过空白符 return 0; } ``` 输入空格时,输出其ASCII码(32)[^2]。 - **使用`getline`读取整行**:对于包含空格的字符串(如句子),使用`std::getline`(需包含`<string>`)。 ```cpp #include <iostream> #include <string> int main() { std::string fullName; std::cout << "Enter your full name: "; std::getline(std::cin, fullName); // 读取整行(包括空格) std::cout << "Hello, " << fullName << "!" << std::endl; return 0; } ``` 输入`John Doe`会完整输出。注意:`cin >>`和`getline`混用时,需用`cin.ignore()`清除换行符残留。 #### 4. **最佳实践和常见陷阱** - **输入验证**:始终检查`cin.fail()`或使用`if (std::cin >> num)`(返回流状态),确保数据有效。 - **缓冲区管理**:在循环输入前,用`cin.ignore()`清除旧数据,避免残留字符影响。 - **性能考虑**:`cin`在读取大量数据时可能比C风格函数(如`scanf`)慢,但更安全(类型安全)。 - **错误处理完整示例**: ```cpp #include <iostream> #include <limits> int main() { int age; std::cout << "Enter your age: "; while (!(std::cin >> age) || age < 0) { // 验证输入为整数且非负 std::cin.clear(); std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n'); std::cout << "Invalid input! Enter a positive integer: "; } std::cout << "Age: " << age << std::endl; return 0; } ``` 通过以上步骤,您可以安全地使用`cin`进行各种输入操作。记住,正确处理错误和空白符是关键,以避免程序崩溃或逻辑错误[^1][^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值