std::cin >> 读整数遇非法字符(如字母)会置 failbit,后续读取全部失败;必须先 cin.clear() 清标志位,再 cin.ignore(numeric_limits<streamsize>::max(), '\n') 清缓冲区,或改用 getline + stringstream 解析以避免流污染。

输入时遇到非数字字符会卡住或跳过后续读取
用 std::cin >> 读整数数组时,如果用户输了个字母(比如 a 或 12x34),std::cin 会进入失败状态(failbit 被置位),后续所有读取都直接失败,但程序不会报错、也不会自动跳过——它就停在那儿,像冻住一样。
常见现象:输入 1 2 x 4 后,只读到 1 和 2,然后 x 卡住,4 永远读不到;或者循环直接退出,数组后面全是 0。
- 必须手动检查
std::cin.fail(),不能依赖“读完就完事” - 出错后要用
std::cin.clear()清除错误标志 - 再用
std::cin.ignore()跳过当前行剩余字符(否则非法字符一直留在缓冲区) - 推荐忽略到换行符:
std::cin.ignore(std::numeric_limits<:streamsize>::max(), '\n')</:streamsize>
用字符串解析替代直接流提取更可控
绕过 std::cin >> 的脆弱性,先把整行读成 std::string,再用 std::stringstream 或 std::stoi 逐个解析。这样非法字符不会污染整个流状态,还能精确知道错在哪一列。
示例场景:用户输入 "10 abc 20 -5",期望跳过 abc 并继续读后面的数字。
立即学习“C++免费学习笔记(深入)”;
- 用
std::getline(std::cin, line)读整行,避免残留问题 - 对每个 token 调用
std::stoi(token, &pos),检查pos == token.size()确认全部转换成功 - 若
std::stoi抛std::invalid_argument或std::out_of_range,捕获并跳过该 token - 注意:空格、制表符、多个连续空格都会被
std::stringstream自动跳过,但std::stoi不处理前导空格,需先token.erase(0, token.find_first_not_of(" \t"))
scanf("%d", &x) 遇到非法输入返回值是 0,不是 EOF
C 风格的 scanf 在 C++ 里仍可用,但它对非法输入的反馈更明确:成功读一个整数返回 1,遇到非数字字符返回 0,遇到文件结束返回 EOF。很多人误以为返回 0 是“读完了”,其实那是“根本没读到有效数”。
- 必须检查返回值:
if (scanf("%d", &x) != 1) { /* 出错了 */ } - 返回 0 时,非法字符仍留在 stdin 缓冲区,得手动清理:
int c; while ((c = getchar()) != '\n' && c != EOF); - 不要混用
scanf和std::cin——它们缓冲区不共享,混用极易丢数据或阻塞 -
scanf不支持宽字符或 locale 感知的数字格式(比如带逗号的千分位),纯 ASCII 场景下才推荐
边界情况:负号、+号、科学计数法、溢出怎么判
用户输入 "-123"、"+456"、"1e3" 或超大数如 "99999999999999999999" 时,不同方法行为差异很大。
-
std::stoi支持+/-前缀,但不支持e或E(会截断,"1e3"变成1) -
std::stol/std::stoll同样不解析科学计数法;要支持浮点形式,得用std::stod再转整,但会损失精度 - 溢出时
std::stoi抛std::out_of_range,必须 try-catch;scanf对溢出行为未定义(可能返回值异常或写脏内存) - 真正健壮的做法:用
std::from_chars(C++17),它不抛异常、能精确返回解析终点和错误码,且不依赖 locale
实际写数组输入时,别指望一次读完就万事大吉。最常被忽略的是:清缓冲区这一步,很多人只 clear() 不 ignore(),结果下一轮读取立刻又 fail;还有人用 cin >> ws 想跳空白,但它跳不过字母。


















