我正在尝试使用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。现在以上所有内容都适用。
我已经用调试器测试了我的程序一个小时了,这似乎是唯一合理的解释。我的最佳猜测是,这种行为对于其他编译器或操作系统来说会有所不同,这取决于流的处理方式。