Infinite loop when entering a character?

Hopefully this will be a simple topic. I'm working on loops. I've found that loops work great as long as you don't enter a character when the loop expects an integer. For example, take this program:
1
2
3
4
5
6
7
8
9
10
11
12
13
#include <iostream>

int main()
{
	int num;
	do
	{
	std::cout << "Enter 1 to quit, anything else to keep going: ";
	std::cin >> num;
	}while(num != 1);
	 
	return 0;
}

It works really well if I input any numbers-- if I input 1, the program ends, any other number-- even negative numbers-- and it keeps going. However, if I input f, when it asks me to enter a number, and I press Enter, the program enters into an infinite loop and I have to exit the program by clicking on the X on the program box. Why is that? I made another program without a loop to see what happens when I enter f when the program asks for an int:
1
2
3
4
5
6
7
8
9
10
11
#include <iostream>

int main()
{
	std::cout << "Enter a number: ";
	int num;
	std::cin >> num;

	std::cout << "You enter " << num << "!\n";
	return 0;
}

I entered f here, and the output was:
Enter a number: f
-858993460
Press any key to continue . . .

Is that a random memory address? I went back to the loop I made and entered the value -858993460 when asked to enter a number, and it didn't cause any problems-- just treated it like any other number. Why does it cause an infinite loop if you enter a character in a loop that expects an integer? What validation code could you make to ensure the user is not able to enter a char when the loop asks for an int to avoid an infinite loop?
Also, I just made this program to see if entering a number when the program expects a character causes the same problem:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>

int main()
{
	char letter;

	do
	{
	std::cout << "Enter 'a' to quit, any other letter to keep going: ";
	std::cin >> letter;
	}while(letter != 'a');

	//std::cout << letter << std::endl;

	return 0;
}

But it doesn't! I can enter letters or numbers all day without a problem (no infinite loops). To validate an int, should you just accept a number into a char and convert it or something-- to avoid infinite loops?
Last edited on
closed account (48T7M4Gy)
http://www.cplusplus.com/forum/general/207242/#msg979104

Go back to this thread of yours. The stream cin is put into an error state if the input value isn't the expected type. To recover you need to clear the error state and then ignor the rest of the line hidden in the incorrect input including the en of line character.

You don't need to do conversions, just predict and trap user input errors.

PS The large number is just junk undefined behaviour stuff as a result of the incorrect input. ie cin expects an int and got a char.

PPS If you want to be really smart you can use the error as a mechanism for quitting. Type in a char to terminate a series of integer inputs by deliberately making an error. But make sure that doesn't make follow on programming go haywire because the clear and ignore haven't been placed properly if needed.
Last edited on
I saw you put that code there, but I didn't understand it at first . . . Turns out it's a hidden gem that I really needed! I wrote a new loop that works!
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <iostream>

int main()
{
	int num;

	do
	{
	std::cout << "Enter 1 to quit, anything else to keep going: ";
	if(!(std::cin >> num))//I copied this from elsewhere. It means that if cin does not read
	{                               //the expected type, it will cause an error
		std::cout << "Please enter numbers only: ";
		std::cin.clear();
		std::cin.ignore(10000, '\n'); 
	}

	if(num != 1)
		std::cout << "You entered " << num << std::endl;

	}while(num != 1);

	std::cout << num << std::endl;

	return 0;
}


But if cin.clear() clears the buffer, why do you need the cin.ignore statement? And what does the error state look like in the buffer (what does the buffer hold that causes an infinite loop? And how does if(!(std::cin >> num)) mean that it won't except answers of the wrong type? To me, it looks like that statement should mean, if cin fails to read data into num, produce an error-- but how would cin fail? So many questions. Sorry!
Last edited on
closed account (48T7M4Gy)
No, cin.clear() clears the error state, not the stream buffer. So when the cin stream fails because of the input type error it sets a failure flag/bit and clear() is like a reset of that flag/bit.

cin.ignore(), for want of a better description clears out any stuff (including stuff you repeated mistakenly thinking you just punch it again and everything will sort itself out) in the cin stream itself so that everything wrong in the stream is gone plus everything else, ready for the next attempt at getting it right. If you don't do that any junk typed in will still be there for reading end.

if(!cin etc ... basically says if cin didn't work or failed do what's inside the while loop and if it does work test the other things there, and act accordingly. The only way you'll get outside the loop is doing the input properly ... if that makes sense.

cin fails because the variable being read in is previously declared as a particular type so that what it checks for. If you type in something which doesn't satisfy that type then it fails, is no good etc etc. (As a bonus sometimes you might want to force an error ... ).

http://stackoverflow.com/questions/5131647/why-would-we-call-cin-clear-and-cin-ignore-after-reading-input

These 2 are there if you want to get into it but are more terse:
http://en.cppreference.com/w/cpp/io/basic_ios/clear
http://en.cppreference.com/w/cpp/io/basic_istream/ignore
Last edited on
Dude, I don't know who you are, but you are awesome. This is helping me a lot. Thanks kemort!
closed account (48T7M4Gy)
My pleasure. Everybody here enjoys helping where they can. Cheers.
I know I already asked you a lot, but you think you could help me with this (it's probably an easy fix):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#include <iostream>
#include <iomanip>
#include <ctime>
#include <cstdlib>

int main()
{
	std::cout << "What is the starting number of organisms: ";
	int beginning_population;
	//std::cin >> beginning_population; <--- I tried this line but it didn't change anything

	while(!(std::cin >> beginning_population) || beginning_population < 2)
	{
		if(!(std::cin >> beginning_population))
		{
			std::cout << "Please enter numbers only! Re enter the starting population: ";
			std::cin.clear();
			std::cin.ignore(10000, '\n');
		}
		else if(beginning_population < 2)
		{
			std::cout << "Do not enter less than 2 for beginning population. "//this doesn't print
					  << "Re enter the beginning population: ";//and the loop never iterates again
			//std::cin.clear();// these didn't change anything either
			//std::cin.ignore(10000, '\n');
		}
		
	}

	std::cout << "You entered " << beginning_population << " which is acceptable.\n";
     return 0;
}

As you can see, I'm trying to get input validation so that if an inaccurate type is entered for beginning_population or if beginning_population is less than 2, an error will occur notifying the user specifically what the error was. I really thought this code would work, and it does for validating bad input, but if less than two is entered, the else if statement does not execute-- I can input: 1 [Enter] and nothing happens, just keep pressing enter and nothing comes up. I can't even tell what's going on internally (what's in the buffer/why it won't print/etc) Maybe it has something to do with the way I'm getting input from the user? I don't know, any ideas?






In this last post of yours, you are getting input twice for every iteration of the loop.

You already know inside the loop that beginning_population is less than two or that the input operation failed.

Inside the loop you can check the state of cin without trying to extract further input.

12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
	while(!(std::cin >> beginning_population) || beginning_population < 2)
	{
		if(!std::cin)
		{
			std::cout << "Please enter numbers only! Re enter the starting population: ";
			std::cin.clear();
			std::cin.ignore(10000, '\n');
		}
		else // if(beginning_population < 2) <- you already know this is true, no reason to re-check it.
		{
			std::cout << "Do not enter less than 2 for beginning population. "//this doesn't print
					  << "Re enter the beginning population: ";//and the loop never iterates again
			//std::cin.clear();// these didn't change anything either
			//std::cin.ignore(10000, '\n');
		}
		
	}
Ah I see, rookie mistake. I've never used std::cin to simultaneously get input and check for validation so that's why I didn't recognize that it was getting input within the loop also. Thanks a lot!
closed account (48T7M4Gy)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>

/*
 RULE:
 beginning_population is an int
 and
 beginning_population is >= 2
 */

int main()
{
    int beginning_population = 0;
    
    while
        (
          std::cout << "Enter beginning population: "
          and
          (
           !(std::cin >> beginning_population)
           or
           (beginning_population < 2)
           )
          )
    {
        std::cout << "Error: \n";
        
        if(std::cin.fail())
        {
            std::cout << "Please enter numbers only!\n";
            std::cin.clear();
            std::cin.ignore(10000, '\n');
        }
        else
            std::cout << "Do not enter less than 2 for beginning population\n";
    }
    
    std::cout << "You entered " << beginning_population << " which is acceptable.\n";
    return 0;
}


One small point to consider is how much trouble you want to go to in giving feedback to the user. The point is similar to logon errors. Often the feedback is just "try again" rather than saying which of the user name or password or both is wrong. An adequate general message saves the tedium of pretty useless (or even dangerous) feedback.
Last edited on
Topic archived. No new replies allowed.