从STDIN读取int时的奇怪行为

Ozz*_*zah 5 c++ stdin menu infinite-loop

假设我们有一个菜单向用户显示一些选项:

Welcome:
1) Do something
2) Do something else
3) Do something cool
4) Quit
Run Code Online (Sandbox Code Playgroud)

用户可以按1 - 4然后按回车键.程序执行此操作,然后将菜单返回给用户.无效选项应该只显示菜单.

我有以下main()方法:

int main()
{
    while (true)
        switch (menu())
        {
            case 1:
                doSomething();
                break;
            case 2:
                doSomethingElse();
                break;
            case 3:
                doSomethingCool();
                break;
            case 4:
                return 0;
            default:
                continue;
        }
}
Run Code Online (Sandbox Code Playgroud)

以下是menu():

int menu()
{
  cout << "Welcome:" << endl
       << "1: Do something" << endl
       << "2: Do something else" << endl
       << "3: Do something cool" << endl
       << "4: Quit" << endl;

  int result = 0;
  scanf("%d", &result);
  return result;
}
Run Code Online (Sandbox Code Playgroud)

输入数字类型效果很好.输入1 - 4会使程序执行所需的操作,然后再次显示菜单.输入此范围之外的数字(例如-1或12)将再次按预期显示菜单.

但是,输入类似"q"的内容只会导致菜单无限地反复显示,甚至无法停止获取用户输入.

我不明白这是怎么发生的.很明显,menu()正在调用菜单,因为菜单会一遍又一遍地显示,但却scanf()是其中的一部分menu(),所以我不明白程序如何进入这种错误状态,用户不会被提示输入.

我最初做过的cin >> result是完全相同的事情.

编辑:似乎有一个相关的问题,但原始源代码已从pastebin中消失,其中一个答案链接到一篇文章,显然曾经解释过为什么会这样,但现在是一个死链接.也许有人可以回复为什么会发生这种情况而不是链接?:)

编辑:使用这个例子,这是我解决问题的方法:

int getNumericalInput()
{
    string input = "";
    int result;

    while (true)
    {
        getline(cin, input);

        stringstream sStr(input);
        if (sStr >> result)
            return result;

        cout << "Invalid Input. Try again: ";
    }
}
Run Code Online (Sandbox Code Playgroud)

而我只是更换了

int result = 0;
scanf("%d", &result);
Run Code Online (Sandbox Code Playgroud)

同

int result = getNumericalInput();
Run Code Online (Sandbox Code Playgroud)

Jer*_*fin 6

当您尝试将非数字输入转换为数字时,它会失败并且(重要部分)将该数据保留在输入缓冲区中,因此下次您尝试读取int时,它仍然在等待,并再次失败 - - 再一次,永远.

有两种基本方法可以避免这种情况.我更喜欢的是读取一串数据,然后从中转换为数字并采取适当的操作.通常,您将使用std::getline读取所有数据直到换行,然后尝试转换它.因为它会读取正在等待的任何数据,所以你永远不会在输入中"卡住"垃圾.

替代方案(特别是如果转换失败)用于std::ignore从输入读取数据(通常)到下一个换行符.