C++简单的高低游戏的意外行为



我正在尝试使用learncpp.com学习cpp。到目前为止,我对教程系列本身没有任何疑问。

在第3章之后,我尝试编写自己的简单Hi-Lo游戏,以测试我是否足够了解基本知识,可以完全自己编写一个非常简单的程序。

我将把我的代码文件放在这里,然后我将解释意外行为。

Main.cpp:

#include "io.h"
#include "math.h"
#include <string>
int main() {
int x{ getNumberFromConsole() };
int target{ getRandomInt() };

while (x != target) {
if (x > target) {
printToConsole("Lower");
} else if (x < target) {
printToConsole("Higher");
}
x = getNumberFromConsole();
}
printToConsole("Correct, the answer is:");
printToConsole(std::to_string(target));
return 0;
}

math.h:

#ifndef MATH_H
#define MATH_H
int getRandomInt();
#endif

math.cpp:

#include <cstdlib>
#include <ctime>
int getRandomInt() {
srand(time(NULL));
int randomNumber{ rand() % 100 };
return randomNumber;
}

io.h:

#ifndef IO_H
#define IO_H
#include <string>
int getNumberFromConsole();
void printToConsole(std::string x);
#endif

io.cpp:

#include <iostream>
#include <string>
int getNumberFromConsole() {
int x{};
std::cout << "Type a number: ";
std::cin >> x;
return x;
}
void printToConsole(std::string x) {

std::cout << x << "n";
}

我目前配置的编译器如下(如果这很重要的话):

PS C:UsersADMIN_VMDocumentsC++helloworldhi-lo-attemptoutput> g++ --version
g++.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

现在针对意外行为:当我运行";游戏";只要按我应该的方式在终端中输入整数,游戏就可以从头到尾按预期进行。

问题是:当我输入一个字符串(字符)时。例如:";K";。我预料到;游戏";将字母K视为值为0的整数。当我输入字母"0"时,调试器也确认了这一点;K〃:x的值为0。

本系列教程本页的测验部分还指出,当输入H等字符时,整数的值将为0:https://www.learncpp.com/cpp-tutorial/introduction-to-iostream-cout-cin-and-endl/

实际发生的是游戏状态";更高";(正如预期的那样,0可能低于目标),但游戏并没有允许用户输入另一个值,而是陷入了循环,并不断打印Type a number: Higher

我真的不明白为什么会发生这种事。我本以为我的程序会崩溃,或者它会要求一个新的数字,并将字符视为0。

在测试中,我得出结论,我能想到的唯一解释是,我的函数getNumberFromConsole()以某种方式从以前调用的函数printToConsole(std::string x)中获取输出,但这对我来说似乎不符合逻辑

有人能向我解释一下这里到底发生了什么,为什么要这样做吗?

我知道头文件和多个cpp文件对于像这样一个琐碎的程序来说可能太夸张了,但我想训练我迄今为止学到的一切。

我真的不明白是什么导致了这种行为,我很想知道是什么导致的

值得一提的是,我从不使用类似cin>gt;someInt。我总是自己解析,这样我就可以自己处理错误了。因此,我会将输入作为:

string line;
getline(cin, line);

然后我编写方法来验证行(在这种情况下)只包含数字。然后,您就可以随心所欲地报告错误,还可以进行所需的所有其他控制。

这是您代码中的另一个问题:

int getRandomInt() {
srand(time(NULL));
int randomNumber{ rand() % 100 };
return randomNumber;
}

您只希望srand()调用一次。在调用getRandomInt()之前,将其移动到main()中。您正在做的是一次又一次地重新设定随机数生成器的种子。你不想那样做。只做一次。

我发现了导致我出现这种意外行为的原因。一开始我看错了地方。

X的值实际上不是这里的问题,问题在我的getNumberFromConsole()函数中。为了方便起见,我将把第一个io.cpp文件的内容再次粘贴到这里。

io.cpp:

#include <iostream>
#include <string>
int getNumberFromConsole() {
int x{};
std::cout << "Type a number: ";
std::cin >> x;  // This line causes the issue
return x;
}
void printToConsole(std::string x) {

std::cout << x << "n";
}

在阅读了@PaulMcKenzie发布的评论中的链接后,我了解到std::cin不会简单地忽略错误的输入,并在再次调用时等待新的输入。它以某种";出现错误";状态

当我输入值"0"时,实际发生了什么(如果我理解正确的话);h";,不能满足行std::cin >> x:h不是整数,因此cin输入一个"n";出现错误";状态

它忽略任何新的输入;冻结";输入流,所以每次我的循环再次运行时,函数getNumberFromConsole()都不允许用户输入新值。相反,std::cin >> x将再次从冻结流中读取最后一个条目(在这种情况下为"h"),这又保持了"h";出现错误";状态活动。

解决这个问题的方法是首先";清除";cin,这将删除errorstate,并允许您再次使用它的输入流。CCD_ 9本身清除cin是不够的。";坏的";条目(h)仍在此流中,并且将被读取并在while循环的下一次迭代中导致错误。

为了避免这种情况,可以使用std::cin.ignore(x),其中x表示要忽略的输入字符数。这里的问题是,尽管在这种情况下它会去掉"h",但这不是一个好的解决方案。

如果用户例如输入";这不是一个整数";,您必须根据输入字符串的长度调整ignore函数的值。

我在这里使用的修复程序是:

if (std::cin.fail()) {  // Check if the input stream is in an error state
std::cin.clear();  // if so, unfreeze the stream
std::cin.sync();  // "sync" the stream to ignore any earlier input, waiting for new input to be given.
}

这允许循环重复,而不会停留在先前输入的字符上。

新的io.cpp现在是:

#include <iostream>
#include <string>
int getNumberFromConsole() {
int x{};
std::cout << "Type a number: ";
std::cin >> x;
if (std::cin.fail()) {
std::cin.clear();
std::cin.sync();
}
return x;
}
void printToConsole(std::string x) {

std::cout << x << "n";
}

需要注意的是,当输入字符串时,使用此方法仍然会将X的值视为0。

附言:经过更多的测试,我注意到这个方法确实像预期的那样适用于字符串,但它仍然使循环";错误";输入浮点值时两次。这仍然让我感到困惑。

[EDIT]:我想好了:如果输入2.33,数字2适合int x,小数点后的部分保留在缓冲区中,迭代继续。

在下一次迭代中:cin检查流,发现现在只有.33,它不适合INT.Error。现在以上所有内容都适用。

我已经用调试器测试了我的程序一个小时了,这似乎是唯一合理的解释。我的最佳猜测是,这种行为对于其他编译器或操作系统来说会有所不同,这取决于流的处理方式。

最新更新