isdigit not working with map

So my program runs... kind of. If I assume the user inputs an integer it works fine, however if they input a char or anything else really, the whole program will execute through and its really weird and dont understand it, so, I decided to add in a check using the isdigit function but it sends me into some weird sort of infinite loop.
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
#include <iostream>
#include<iterator>
#include<map>
#include<string>
using namespace std;



int main()
{
map<string, int>math;
int answer;
//bool check;
math.insert(pair<string,int> ("What is 1 + 1 = ? ", 2));
math.insert(pair<string,int> ("What is 2 + 2 = ? ", 4));
math.insert(pair<string,int> ("What is 3 + 3 = ? ", 6));

map<string, int>::iterator it;


for (it = math.begin(); it != math.end(); it++){
    cout << it->first;
    cin >> answer;
    /*check = isdigit(answer);
    while (check){
        cout << "invalid response type, please tell enter an int";
        cin >> answer;
    }*/
    if (answer != it->second){
        cout << "Incorrect, the correct answer is : " 
        << it->second << endl;
    }
    else{
        cout << "Correct!" << endl;;
    }
}
}
Also I know that I didnt have to set a separate bool variable and could have just done while(isdigit(answer)), but I was just messing around trying different things.
isdigit is for checking whether you input the chars '0' through '9'.

Your code should be like
1
2
3
4
5
6
7
8
int answer;
if ( cin >> answer ) {
  // it's a good number, do stuff with it.
} else {
  // tidy up the input stream because of the failed input
  // https://en.cppreference.com/w/cpp/io/basic_istream/ignore
  cout << "Type in a number";
}
Last edited on
if isdigit checks if then input was from 0-9 then shouldnt it still work?

also can you explain what happens to my program when I have the while loop in it and enter in a character, I dont understand what the compiler is thinking.
check is set on first input ... it is never updated. So you would go on for ever with that false value, which is all the loop test sees.
> if isdigit checks if then input was from 0-9 then shouldnt it still work?
isdigit works on characters, not integers.

So 0 != '0'

Sure, if you typed in a number between 48 and 56, then this would work.
1
2
3
4
5
int number;
cin >> number;
if ( isdigit(number) ) {
  cout << "Mmm, interesting...";
}

But that's nothing to do with the fact that you typed in the characters '4' and '8' for example.

You just picked integers that correspond to the ASCII values of the character '0'.


1
2
3
4
5
    /*check = isdigit(answer);
    while (check){
        cout << "invalid response type, please tell enter an int";
        cin >> answer;
    }*/
This will lead to an infinite loop or no loop at all because check is not modified inside the loop.
I guess part of the confusion might be that if you look at the docs it says that isdigit expects an int as argument.

int isdigit ( int c );

https://cplusplus.com/reference/cctype/isdigit/

But it actually expects you to pass a char. And the int that it returns is meant to be treated as a bool. Note that both char and bool are implicitly convertible to/from int so this works without having to do anything special most of the time. I think the reason why they choose to do it like this has to do with the fact that old C (from where this function originates) didn't have a bool type and for consistency with other functions that work on chars and could be passed the special value EOF that is outside the char range of values.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <cctype>

int main()
{
	char ch;
	while (std::cin >> ch)
	{
		if (std::isdigit(ch))
		{
			std::cout << "'" << ch << "' is a digit.\n";
		}
		else
		{
			std::cout << "'" << ch << "' is not a digit.\n";
		}
	}
}
Use Ctrl+Z (Windows) or Ctrl+D (elsewhere) to exit the loop.

char is actually an integer type. Each number represents a character. Look at an ASCII table to see how it's usually done: https://www.asciitable.com/
Note that the character '0' is represented as the value 48.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>

int main()
{
	std::cout << "Enter a character: ";
	char c;
	std::cin >> c;
	
	std::cout << "Enter an integer: ";
	int i;
	std::cin >> i;
	
	std::cout << "You have entered the character '" << c << "' which is represented by the character code " << int(c) << ".\n";
	std::cout << "You have also entered the integer " << i << ".\n";
	if (c == i)
	{
		std::cout << "'" << c << "' and " << i << " is the same value (but the type is different).\n";
	}
	else
	{
		std::cout << "'" << c << "' and " << i << " are two different values.\n";
	}
}
Enter a character: 5
Enter an integer: 5
You have entered the character '5' which is represented by the character code 53.
You have also entered the integer 5.
'5' and 5 are two different values.
Enter a character: 7
Enter an integer: 55
You have entered the character '7' which is represented by the character code 55.
You have also entered the integer 55.
'7' and 55 is the same value (but the type is different).

So when you use cin to read an integer you won't get the character representation (what would that even mean if the user entered an integer with multiple digits?).
You will instead get the integer representation and passing that to isdigit doesn't make sense.

Note that an int variable can only contain integer values. You can never try to read a value and then check the value to see if it's an integer. An int ALWAYS has an integer value. What you need to do instead is to check if cin failed. There are a few different ways you can do this...

1
2
3
4
5
6
int answer;
std::cin >> answer;
if (std::cin.fail())
{
	// deal with incorrect input...
}

1
2
3
4
5
6
int answer;
std::cin >> answer;
if (!std::cin)
{
	// deal with incorrect input...
}

1
2
3
4
5
int answer;
if (!(std::cin >> answer))
{
	// deal with incorrect input...
}

All of these do the same thing. Normally I prefer the last one. It's especially convenient when writing it as a loop.

If an input operation fails cin will enter a "fail state" and any input operation that follows will automatically fail. What you normally need to do is to first get out of the "fail state" and then remove the incorrect input before continuing.

1
2
3
4
5
6
7
int answer;
if (!(std::cin >> answer))
{
	std::cout << "You entered an invalid integer!\n";
	std::cin.clear(); // leave "fail state"
	std::cin.ignore(1000, '\n'); // discard whole line of input (or up to 1000 character whichever comes first)
}

Going back to your code...

You might want to replace line 23-28 with something like the following.

1
2
3
4
5
6
while (!(cin >> answer))
{
	cout << "invalid response type, please tell enter an int";
	cin.clear();
	cin.ignore(1000, '\n');
}

Note that if you forgot to call clear() here the ignore and >> would continue to fail forever which would result in an infinite loop.
Last edited on
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
#include <iostream>
#include <map>

int main()
{
    const std::map< std::string, int > math_questions =
    {
        { "What is 1 + 1 = ? ", 2 },
        { "What is 7 - 3 = ? ", 4 },
        { "What is 5 * 6 = ? ", 30 },
        { "What is 995 + 106 = ? ", 995+106 }
    };

    // range based loop with a structured binding declaration
    // https://www.stroustrup.com/C++11FAQ.html#for
    // https://en.cppreference.com/w/cpp/language/structured_binding
    for( const auto& [ question, correct_answer ] : math_questions )
    {
        std::cout << '\n' << question ;
        int answer = 0 ;

        // if we have an integer input as the answer and the answer is correct
        if( std::cin >> answer && answer == correct_answer ) std::cout << "correct\n" ;

        else // otherwise (wrong answer or non-integer input)
        {
            std::cout << "incorrect, the correct answer is : " << correct_answer << '\n' ;
            std::cin.clear() ; // clear a possible input error state
            std::cin.ignore( 1'000'000, '\n' ) ; // and throw this bad input line away
        }
    }
}
It's often useful to have a function that displays a prompt and obtains a valid numeric input of a required type. See under input/output on https://cplusplus.com/forum/lounge/279954/

PS. Even using an iter loop (range-for is preferred though), it's easier to use auto rather than a specific type:

 
for (auto it = math.begin(); it != math.end(); it++){


You then don't need L18 and don't haver to remember the type of an iter!
Last edited on
Topic archived. No new replies allowed.