Need some help finding a logical error in my while loop - c++

I am learning C++, and I am doing some exercises in the book I am using. One of them asks to write a program that asks a user how many numbers they want to add up. Then prompt for the numbers the user wants to add or to enter '|' once finished. The numbers are then pushed into a vector. Part of the program asks to check if the size of the vector is equal to the original number of input items and that is where I keep getting an error.
cout << "Please enter the numbers and | once you are done: ";
while(true)
{
for(int num; cin >> num; )
{
if(num == '|')
{
break;
}
ints.push_back(num);
}
if(ints.size() != n)
{
cout << "There are more or less numbers in the vector than originally specified\n"
<< "Vector will be cleared; please re-enter the values: ";
ints.clear();
continue;
}
else
{
break;
}
}
The problem is that if the number of input is off, the message goes into an infinite loop and I am not sure how to fix it.
EDIT: n is the variable that holds in the number of values user wanted to enter.
Thanks!

num is an integer and cin >> num won't extract | symbol. Comparison num == '|' may not work as expected because num could have the numeric value of | ascii symbol even when user did not input any | symbol. You should properly handle end marker reading:
// loop will break when user enters `|` because it is not an integer
// setting failbit of cin
for(int num; cin >> num;)
{
ints.push_back(num);
}
cin.clear(); // reset failbit making cin able to read again
// check the end marker entered by user
{
string end_marker;
cin >> end_marker;
if("|" != end_marker)
{
// use of endl will flush the stream ensuring that
// printed text won't stuck in the buffer
cout << "Please use | as end marker" << endl;
continue;
}
}

Here is how I implemented it. I am worried about the logic in your while loop. I had been taught to avoid while(true) whenever possible. You know the logic behind how your code should work. With more practice you'll start to recognize the conditions you need to use. I am sure there are better ways to do it. But this is the way I tried it.
But to answer your question, the main reason it is failing is because integers cannot compare themselves with characters.
if(num == '|')
That does not work since num is an integer and not a character.
Normally I would implement this in a class and since global variables are not highly looked upon I created my own namespace. You'll have to finish the rest of the logic yourself however:
#include <iostream>
#include <vector>
#include <string>
namespace global
{
std::vector<std::string> strings;
std::vector<int> ints;
std::string a = " ";
int num = 0;
}
void doWork()
{
std::cout << "Please enter the number of integers you would like to add up: ";
std::cin >> global::num;
std::cout << "Please enter the numbers and | once you are done: ";
while (global::a != "|")
{
std::cin >> global::a;
global::strings.push_back(global::a);
}
global::strings.pop_back();
for(auto &e : global::strings)
{
global::ints.push_back(std::stoi(e));
}
}
int main()
{
doWork();
if(global::ints.size() != global::num)
{
std::cout << "Size of vector does not match the size specified. Clearing vector" << std::endl;
global::ints.clear();
global::strings.clear();
global::num = 0;
global::a = " ";
doWork();
}
}
I made a vector of char's and converted those into integers so that way you could add them up. The while loop should be checking for | rather than always running true. It then will check the size of the vector in the end, clear it if it does not match, and ask you to do it again. This is the best way that I could think of doing it.
EDIT: as VTT pointed out, char can only do one character at a time. I have converted it into a string in order to handle the conversion.
EDIT 2: reset the values of global::num and global::a to their default at the end of the failure in order to prevent crashing.

Related

Infinite loop created when inputting "yy" into a char variable that should only take a single character such as 'y' or 'n', "nn" does not break code

The code in the cont function asks the user if they want to play my game again.
The code works when receiving proper character inputs such as 'y' or 'n' as well as their respective capital letter variants, and the else block works properly to loop the function if an invalid input such as 'a' or 'c' is entered.
However during a test run, an input of 'yy' breaks the code causing the program to infinitely loop, running not only this cont function but my game function as well.
choice is stored as a char variable. I am wondering why the code even continues to run upon inputting multi-character inputs such as 'yy' or 'yes'. What's interesting is 'nn', 'ny' and other variations of multi-character inputs that begin with 'n' causes no issues and properly results in the else if block running as intended. Which prints "Thanks for playing." then ends the program.
Can variables declared as char accept inputs greater than 1 character? Does it only take the first value? And if so why does 'yy' cause a loop rather than the program running as intended by accepting a value of 'y' or 'Y'? How can I change my program so that an input of 'yy' no longer causes issues, without specific lines targeting inputs such as 'yy' or 'yes'.
#include <iostream>
#include <string> // needed to use strings
#include <cstdlib> // needed to use random numbers
#include <ctime>
using namespace std;
// declaring functions
void cont();
void game();
void diceRoll();
// variable declaration
string playerName;
int balance; // stores player's balance
int bettingAmount; // amount being bet, input by player
int guess; // users input for guess
int dice; // stores the random number
char choice;
// main functions
int main()
{
srand(time(0)); // seeds the random number, generates random number
cout << "\n\t\t-=-=-= Dice Roll Game =-=-=-\n";
cout << "\n\nWhat's your name?\n";
getline(cin, playerName);
cout << "\nEnter your starting balance to play with : $";
cin >> balance;
game();
cont();
}
// function declaration
void cont()
{
cin >> choice;
if(choice == 'Y' || choice == 'y')
{
cout << "\n\n";
game();
}
else if (choice == 'N' || choice == 'n')
{
cout << "\n\nThanks for playing.";
}
else
{
cout << "\n\nInvalid input, please type 'y' or 'n'";
cont(); // calls itself (recursive function!!!)
}
}
void game()
{
do
{
cout << "\nYour current balance is $ " << balance << "\n";
cout << "Hey, " << playerName << ", enter amount to bet : $";
cin >> bettingAmount;
if(bettingAmount > balance)
cout << "\nBetting balance can't be more than current balance!\n" << "\nRe-enter bet\n";
} while(bettingAmount > balance);
// Get player's numbers
do
{
cout << "\nA dice will be rolled, guess the side facing up, any number between 1 and 6 : \n";
cin >> guess;
if(guess <= 0 || guess > 6 )
{
cout << "\nYour guess should be between 1 and 6\n" << "Re-enter guess:\n";
}
} while(guess <= 0 || guess > 6);
dice = rand() % 6+1;
diceRoll();
if (dice == guess)
{
cout << "\n\nYou guessed correctly! You won $" << (bettingAmount * 6);
balance = balance + (bettingAmount * 6);
}
else
{
cout << "\n\nYou guessed wrong. You lost $" << bettingAmount << "\n";
balance = balance - bettingAmount;
}
cout << "\n" << playerName << ", you now have a balance of $" << balance << "\n";
if (balance == 0)
{
cout << "You're out of money, game over";
}
cout << "\nDo you want to play again? type y or n : \n";
cont();
}
void diceRoll()
{
cout << "The winning number is " << dice << "\n";
}
Does it only take the first value?
Yes, the >> formatted extraction operator, when called for a single char value, will read the first non-whitespace character, and stop. Everything after it remains unread.
why does 'yy' cause a loop
Because the first "y" gets read, for the reasons explained above. The second "y" remains unread.
This is a very common mistake and a misconception about what >> does. It does not read an entire line of typed input. It only reads a single value after skipping any whitespace that precedes it.
Your program stops until an entire line of input gets typed, followed by Enter, but that's not what >> reads. It only reads what it's asked to read, and everything else that gets typed in remains unread.
So the program continues to execute, until it reaches this part:
cin >> bettingAmount;
At this point the next unread character in the input is y. The >> formatted extraction operator, for an int value like this bettingAmount, requires numerical input (following optional whitespace). But the next character is not numerical. It's the character y.
This results in the formatted >> extraction operator failing. Nothing gets read into bettingAmount. It remains completely unaltered by the >> operator. Because it is declared in global scope it was zero-initialized. So it remains 0.
In addition to the >> extraction operator failing, as part of it failing it sets the input stream to a failed state. When an input stream is in a failed state all subsequent input operation automatically fail without doing anything. And that's why your program ends up in an infinite loop.
Although there is a way to clear the input stream from its failed state this is a clumsy approach. The clean solution is to fix the code that reads input.
If your intent is to stop the program and enter something followed by Enter then that's what std::getline is for. The shown program uses it to read some of its initial input.
The path of least resistance is to simply use std::getline to read all input. Instead of using >> to read a single character use std::getline to read the next line of typed in input, into a std::string, then check the the string's first character and see what it is. Problem solved.
cin >> bettingAmount;
And you want to do the same thing here. Otherwise you'll just run into the same problem: mistyped input will result in a failed input operation, and a major headache.
Why do you need this headache? Just use std::getline to read text into a std::string, construct a std::istringstream from it, then use >> on the std::istringstream, and check its return value to determine whether it failed, or not. That's a simple way to check for invalid input, and if something other than numeric input was typed in here, you have complete freedom on how to handle bad typed in input.

Validating Integer input using booleans

In my c++ code, I would like to validate my user input to be an int between 1,10 using a do while loop. I am able to validated for integers outside of the range. However if user inputs a float or a letter, it becomes an infinite loop. My idea is to add a condition in my while loop for if the input is not an integer to keep asking for input.
the CAPITAL letters is where I am having trouble.
#include <iostream>
using namespace std;
int main(){
cout << "Welcome, ";
int steps;
int count=0;
do{
cout << "How many? \n";
cin >> steps;
IF (STEPS IS NOT INTEGER==TRUE){
COUNT=1;
}
if (steps <1)
{
cout << "not enough...\n";
}
if (steps > 10){
cout << "too many steps.\n Please pick a lower number of steps.\n\n";
}
} while (steps < 1|| steps >10 || COUNT==1);
//doing stuff with valid input
return 0;
}
Essentially I am trying to add another condition that just returns a boolean. and if the boolean implies that the input is not valid, then it reassigns count to make sure the do while loops continues until the input is valid.
The problem i am working on asks for a max and min steps, since all of them were having a similar problem i tried to simplify it and forgot some of the edits.
You can check whether the input failed, i.e. the user entered something that could not be read as an int like this:
if (cin.fail()) { // in place of IF (STEPS IS NOT INTEGER==TRUE)
cin.clear();
cin.ignore();
cout << "not an integer, try again\n";
continue;
}
This avoids the need for the COUNT variable.
Also, your while condition doesn't appear to match the checks inside the loop. What happens when step is either 9 or 10? You should be consistent with the checks inside the loop.
You could use the ! operator.
For example:
if ( !(std::cin >> steps) )
{
std::cin.clear();
std::cin.ignore();
std::cout << "Incorrect entry. Try again: ";
}
Also consider not using using namespace std;.

Console is being flooded when error checking for things that are not an int

I'm trying to only allow integer values into my program, so I've made the following function. The function is similar to other ones I've seen online, and mine seems to work just fine up until I add an ! in front of it to check if something is not an int.
Function to check if input is an integer:
bool isInteger(std::string s)
{
for (int i = 0; i < s.length(); i++)
{
if (isdigit(s[i]) == false)
{
return false;
}
return true;
}
}
Function being put to use:
int getLevel()
{
int level;
std::cout << "Level One\n";
std::cout << "Level Two\n";
std::cout << "Level Three\n";
std::cout << "Level Four\n";
std::cout << "Level Five\n";
std::cout << "Enter your level (1-5): ";
std::cin >> level;
while (!isInteger(std::to_string(level)) || level < 1 || level > 5)
{
std::cout << "Enter an integer value between 1-5 inclusive: ";
std::cin >> level;
}
clrscr();
return level;;
}
I believe the function works just fine until I put the ! in front of it. I am trying to only allow integer input into my program, and when I enter a double or string, the console becomes flooded with the message "Enter an integer value between 1-5 inclusive: " and doesn't give any time to enter an input. I am fairly new to c++ programming and could use some advice. Thank you!
std::cin >> level;
will try to read an integer and it will never read anything other than an integer. If this fails std::cin's failbit is set and further input operations (like std::cin >> level; inside the loop) are skipped.
You need to check if the reading succeeded and ignore the current input if not. Like this for example:
std::cout << "Enter your level (1-5): ";
while(!(std::cin >> level) || level < 1 || level > 5) {
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Enter an integer value between 1-5 inclusive: ";
}
As little semi-related hint: level will always be an integer. Converting it to a string will always be the string-representation of an integer, so isInteger(std::to_string(level)) will always be true, unless level is negative, because you don't check for the sign.
Also that return true; in isInteger must be outside the loop, else you only check the first character.
Thanks to all the replies and clarification, I've managed to come up with a solution of my own.
New isInteger function that now checks for everything that is needed including inputs like "0004" that a user suggested above:
bool errorCheck(std::string s)
{
int intLevel;
std::stringstream tempLvl(s);
tempLvl >> intLevel;
for (int i = 0; i < s.length(); i++)
{
if (isdigit(s[i]) == false || s[0] == '0' || intLevel < 1 || intLevel > 5)
{
return false;
}
}
return true;
}
The method in action:
std::cout << "Enter your level (1-5): ";
std::cin >> stringLevel;
while (!errorCheck(stringLevel))
{
std::cout << "Enter an integer value between 1-5 inclusive: ";
std::cin >> stringLevel;
}
std::stringstream lvl(stringLevel);
lvl >> level;
clrscr();
return level;
}
Please let me know if you spot any problems with the code or have any easier solutions. Thanks for all the help!
ok i am gonna tell u the fact that console input extracts the input from console so if u ever tried to do something like that
i.e read string in place of integer the cin is going to be in bad state you can check this fact by putting an if like this
if(!cin>>level) break;
and u will find it working actually stream takes input from the console and convert it to boolean value so u can always check it's state bad state return false else true...... ..
SO,finally the bug is in cin>>level...
I hope u understood.... also check out that return true statement..
i am gonna put u reference link for more answer on this bug...
user enters String instead of Int

how to store data into dynamic array

I have this program that asks user to enter a number and ask the user if they want to view input history. So i was wondering if my code is correct. I want to know if doing cin>>num[count-1] is correct or is there a correct way to get the data the user inputs. Here it is:
#include<iostream>
using namespace std;
int main(){
const int size = 20;
int *num = new int[size];
char answer;
int count = 1;
while(true){
cout<<"ENTER NUMBER: \n";
cin>>num[count-1];
cout<<"TRY AGAIN? ";
cin>>answer;
switch(answer){
case 'y':
count++;
system("cls");
break;
default:
cout<<"INPUT HISTORY: \n";
for(int i=0;i<=count-1;i++){
cout<<num[i]<<endl;
}
count++;
}
}
delete [] num;
return 0;
}
I want to know is doing cin>>num[count-1]` is correct or is there a correct way to get the data the user inputs.
Your code is a c-style code. You have the std::array and std::vector to help you to write a more secure and clean code. Because your tag in your question is dynamic-arrays I suggest to use the std::vector.
Bellow you can check out your code could be with the replacement.
#include <iostream>
#include <vector>
using namespace std;
int main() {
//int *num = new int[size]; //normally you don't need to use new. Let the c++ manage it for you
vector<int> num;
char answer;
while (true) {
cout << "ENTER NUMBER: \n";
num.emplace_back(); //Create a new element to vector num
cin >> num.back(); //set this new element
cout << "TRY AGAIN? ";
cin >> answer;
if (answer == 'y')
system("cls");
else {
cout<<"INPUT HISTORY: \n";
for (auto& numEle : num) //It will interate over all elements of num
cout<< numEle <<endl;
//break; //Maybe you want to break the loop here
}
}
// delete [] num;
return 0;
}
I will first show you the problems in your code. I put in comments.
Then I will explain you, why your input is wrong. Also the answer from user TheArquitect is wrong and will finally lead to a memory overflow.
Your program will also have a desastrous result.
First see the code:
#include<iostream>
using namespace std; // In C++ we never use this statement. Why? See:
// You will find at least 1000 comments in Stackoverflow
int main() {
const int size = 20; // In C++ we use constexpr for compile time constants
int* num = new int[size]; // In C++ we
// Do not use raw pointers for owned memory
// Do not use new
// Do Not use C-Style arrays
// Generally: std::vector should be used
// Or at least std::unique_ptr and std::make_unique
// Also, this 2 lines are nonesense. It is the same as int num[20];
char answer; // All variables should always be initialized
// Variables should be in the scope, where they are necessary
int count = 1; // YOu could use uniform initialization
// Arrays start with index 0 in C++ (also in C )
while (true) { // You are creating an endless loop with desastrous effect
cout << "ENTER NUMBER: \n";
cin >> num[count - 1]; // No, this will not work. Explanation in text
cout << "TRY AGAIN? ";
cin >> answer; // No, this will not work. Explanation in text
switch (answer) { // Switch with only one case can always be expressed with if elese
case 'y':
count++;
system("cls"); // This is a non portable solution
break;
default:
cout << "INPUT HISTORY: \n";
for (int i = 0; i <= count - 1; i++) { // Nearly Never use <= in for loops
// Use for (int i = 0; i < count; i++)
// Do use ++i insted of i++
cout << num[i] << endl;
}
count++;
} // This is an endless loop. count will always be increased. If greater than 20
// then the memory will be corrupted. This will happen always
}
delete[] num; // Dead code. Will never be invoked
return 0;
}
OK. Now, besides the major bugs and the killing out of bounds problem, here the explanation for the problem with inputting data.
Rule: You must always check, if the input operation worked. For that you can check the state of the stream. Additionally, if you do not consume the input data (because of an erronous input by the user), this data is still in the input buffer. And in the next loop it will be read again (without waiting for new user input). The status of the input stream may still be wrong and it will also not read because of this.
Then the endless loop starts to run without user input and corrupts your memory.
You may check, by simply inputting a letter instead of a number.
How to fix?
Again, you must check the state of the stream after reading. This is usually done in the form:
if (std::cin >> number)
Why does this work? Because: The inserter operator returns a reference to the stream (so std::cin) and the boolean '!' not-operator for the std::istream is overwritten. It will show, if the state of the stream is still ok or not.
OK, understood. Now. What to do in case of error? 2 operations:
Clear all failure bits in the state of the stream --> std::cin.clear()
Eat up everything that is still in the input buffer --> std::cin.ignore()
You could write:
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
I assume that you are learing C++ in a class and learn about new and delete. As said, you should not use it. But because teacher request this often in the classes, I will show you now a better solution than yours, still using new, but without (most of) the problems.
#include<iostream>
#include <limits>
int main() {
// We want to create a dynamic array with 20 elements
constexpr size_t MaxArraySize{ 20U };
// Allocate a dynamic arry on the heap
int* numberArray = new int[MaxArraySize];
// Read maximum 20 numbers into our dynamic array
for (size_t currentArrayIndex{}; currentArrayIndex < MaxArraySize; ) {
// Inform the user that he should ent now a number
std::cout << "Enter number: \n";
// Read the number and check, if this worked
if (std::cin >> numberArray[currentArrayIndex]) {
// Now ask, if the user wants to continue or stop
std::cout << "Doy you want to enter more numbers? ('y' or 'n'): ";
// Define variable answer and get user input and check, if ok
if (char answer{}; std::cin >> answer) {
// Does the user want to continue?
if ('y' == answer) {
// Yes, continue. Increment array index
++currentArrayIndex;
}
else {
// No, the user does not want to continue. Show the values entered so far
std::cout << "Input History:\n";
for (size_t i{}; i <= currentArrayIndex; ++i) { // NOTE: <= becuase index has not yet been incrementet
std::cout << numberArray[i] << "\n";
}
break; // Stop the for loop and end the program
}
}
else {
// Strange, input of a character did not work. Should not happen. Terminate program
std::cerr << "\n*** Error: Problem with input!\n\n";
break; // Stop the for loop and end the program
}
}
else {
// The user entered someting wrong, or the input did not work for any other reason
// Inform user about problem
std::cerr << "\n*** Error: No number could be read!\n\n";
// Clear error flag and comsume whatever is in the input buffer
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}
}
// Release dynamic allocated memory
delete[] numberArray;
return 0;
}

Making an if condition return if its true or while return if its true

When the condition is true or false, how can I make it return back and ask the question again, making the user re-enter the value?
Here is what I want to implement:
#include <iostream>
#include <string>
#include <sstream>
using namespace std;
int main()
{
int n;
cout<<"Enter numbers. Press 5 to stop: ";
cin>>n;
bool tr=true;
while(tr)
{
if(n!=5)
cout<<"You entered "<<n; //How to make it return again, since its false? I keep getting infinite loops :( ;
else
tr=false;
}
return 0;
}
You need to prompt the user in the while loop, so that it occurs in each iteration:
int n;
bool tr = true;
while(tr)
{
cout << "Enter numbers. Press 5 to stop: ";
cin >> n;
if(n!=5) {
cout << "You entered " << n;
} else {
tr = false;
}
}
Just put all your code (except 'n' and 'tr' definition) in while loop as follow:
int main()
{
int n;
bool tr=true;
while(tr)
{
cout<<"Enter numbers. Press 5 to stop: ";
cin>>n;
if(n!=5)
cout<<"You entered "<<n;
else
tr=false;
}
return 0;
}
The other answers all work, and there is something to be learned about improving program flow from them, but I believe the trick you're asking for is the continue keyword, which skips the remainder of this iteration of the loop.
bool tr = true;
int n;
while (tr)
{
cout << "Enter numbers...";
cin >> n;
if (n != 5)
continue;
else
tr = false;
}
EDIT Part 1: On the continue keyword.
You want to make your code as readable as possible. In this example, its use is unnecessary (as the other posters have shown); but it is the answer to the question "How do I skip the rest of processing in this iteration of my loop and continue to the next iteration?". Usually, such flow-breaking directives actually make code harder to read; but sometimes the opposite is true. Anything (or, at least, almost anything) that can be accomplished with continue or break, can be accomplished without them, so if you're going to use them, you want to have a definite reason for doing so. Usually, when I use continue, it's because I'm looping through a collection of inputs and I want to skip processing the loop whenever the input isn't in the format I'm expecting. Something like this (pseudo-code)...
foreach (Input i in ReceivedInputs)
{
if (i.isBad())
{
cout << "Bad input";
continue;
}
// Do massive block of calculating here.
}
is easier to read than this...
foreach (Input i in ReceivedInputs)
{
if (i.isBad())
cout << "Bad input";
else
{
// Do massive block of calculating here.
}
}
because the second version makes it harder to track what scope you're in, if you're looking toward the end of the massive block of calculating. In this case, I gain code readability by continue, so I use it. But simple code probably shouldn't use it. The break keyword is similar, though it's a lot easier to come up with examples where break is beneficial.
EDIT Part 2: On multiple iterations
This is just an issue of setting up the loop; there are no magic keywords here. The shortest way I can come up with, is probably something like this:
int n = 0;
int numberToTake = 10;
for ( int numbersTaken = 0; numbersTaken < numberToTake; ++numbersTaken)
{
cout << "Enter numbers...";
int n = 0;
for (cin >> n; n != 5; cin >> n)
cout << "Try again.";
// Do whatever processing on n you want to do here.
}
Though I should point out that, doing it this way, the only value you will ever get from the user will be 5, and if he inputs anything that doesn't fit in an integer, you will get unexpected behavior.
EDIT 3: After reading the comment more thoroughly, I think you're just looking for is the more traditional use of the for loop.
No need for the exra bool variable.
The idiom can be: Infinitely loop until the user enters 5:
for(;;) { // Loops infinitely
cout << "Enter numbers. Press 5 to stop: ";
cin >> n;
if(n == 5)
break; // Exits the loop
cout << "You entered " << n; // Before the if if you want to print 5 as well
}