If you enter k
when the input is expecting a number. Then the stream will go into an error state.
The problem is that you did not check the state:
std::cin>>n;
// There could be an error in the line above.
// But you did not check for the error.
Also here:
std::cin>>list[i];
// There could be an error in the line above.
// But you did not check for the error.
Try this:
if (std::cin >> n) {
std::cout << "It worked I got the number: " << n << "\n";
}
else
{
std::cout << "Failed to read a number.\n";
}
How does the above work.
Well the result of the operator>>
is a reference to a stream. So it reads a value from the stream into n
but returns a reference to the stream. This allows you to things like this:
std::cin >> n >> x >> y;
After each operator>>
you get a reference to the stream to apply to the next operator>>
so you can chain reads together.
When you use a stream in a boolean context (a test like an if or while) it will convert itself to boolean value depending on its internal state. If the internal state is good std::cin.good()
then it will return true otherwise it returns false.
So after it completes the operator>>
in then converts itself to bool for the if statement
. If it is in a good state you know the read worked. If the read failed it would set an internal fail state and good()
returns false.
So what happened in your code.
Well the read failed and the state of the stream was set to failed. When a read fails the preferred behavior is that object being read into remain unchanged (this is what happens for POD (standard) types, user defined types this can be a bit more haphazard).
So the value of n
remains unchanged.
When you declared n
int n;
You did not define an initial value so it has an indeterminate value. Which means trying to read that value is UB. UB is bad. it means the code can do anything (which it has done). In practical terms (for most systems) it means the variable has an unknowable value and is whatever was left at that memory location from the last variable that used it.
For your specific case:
So you have typed 5 first then k
.
So your first read std::cin >> n;
worked.
The next read std::cin>>list[i];
failed.
This set the state of the stream to bad. Any subsequent reads do nothing (until you reset the stream state to good). So you are supposed to detect and fix the stream state.
Each subsequent time around the loop the std::cin >> list[i]
will do nothing as the stream is in an error state. Which means it will keep its original value (which for this case is defined as zero 0
).
Again the correct action here is to read and check the state of the stream. If it fails take corrective action:
if (std::cin >> list[i]) {
// Worked
}
else {
std::cerr << "Bad input. Try again\n";
// reset the state of the stream
// before trying to read again.
std::cin.clear();
if (std::cin >> list[i]) {
std::cerr << "You got it correct this time\n";
}
else {
std::cerr << "User not bright enough to use the app aborting\n";
throw std::runtime_error("Failed Bad User");
}
}
Additional Note
This behavior of streams is good for reading user input. As it allows a natural flow for detecting and writing code for the user to fix the issue. This design is practically the same for all modern languages that have the same pattern.
But this is not a good flow when you have machine input (ie. there are not expected to be any errors in the input and if there was an error there is no way to correct it).
For reading machine input you can set the stream to throw on an error. This allows you to write nice clean easy to read code that when things go wrong (when they should not) then an exception is throw causing the application to correctly terminate (or the exception could be caught).
std::cin.exceptions(std::ios::badbit); // Fail and Bad