C++ Input Validation: Handling Bad cin Input
Short answer: test the read with if (std::cin >> value). If it fails, call std::cin.clear() then std::cin.ignore(...) before asking again — otherwise your program spins in an infinite loop.
The Bug Everyone Hits First
#include <iostream>
int main() {
int age;
while (true) {
std::cout << "Enter your age: ";
std::cin >> age;
if (age > 0) break;
std::cout << "Invalid, try again\n";
}
}
Type abc and this prints “Enter your age: Invalid, try again” thousands of times per second, forever.
Here is why. When cin >> age meets characters that are not a number:
- The extraction fails and
ageis left unset (C++11 and later set it to 0). - The offending characters stay in the buffer — they are not consumed.
- The stream enters a fail state, and every future read returns immediately without waiting for input.
So the loop keeps re-reading the same bad characters and never pauses for the user again.
The Fix: clear() and ignore()
#include <iostream>
#include <limits>
int main() {
int age;
while (true) {
std::cout << "Enter your age: ";
if (std::cin >> age) {
if (age > 0) break;
std::cout << "Age must be positive.\n";
} else {
std::cin.clear(); // reset the fail state
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "That is not a number.\n";
}
}
std::cout << "You are " << age << '\n';
}
The two recovery calls do different jobs, and you need both:
clear()resets the error flags so the stream will attempt reads again. On its own it does nothing about the bad characters.ignore(n, delim)throws away up toncharacters, stopping after the delimiter. Usingnumeric_limits<streamsize>::max()means “however many it takes to reach the newline”.
Miss clear() and reads keep failing. Miss ignore() and you re-read the same garbage. That interaction is covered in more depth in cin.ignore and clearing the input buffer.
A Reusable Input Function
Write it once and stop repeating yourself:
#include <iostream>
#include <limits>
#include <string>
int readInt(const std::string& prompt) {
int value;
while (true) {
std::cout << prompt;
if (std::cin >> value) {
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
return value;
}
std::cin.clear();
std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
std::cout << "Please enter a whole number.\n";
}
}
int main() {
int age = readInt("Enter your age: ");
std::cout << "You are " << age << '\n';
}
Note the ignore on the success path too. That discards anything left on the line — so 42abc gives you 42 and cleanly drops the rest, and a following getline is not derailed by the leftover newline.
Validating a Range
int readIntInRange(const std::string& prompt, int lo, int hi) {
while (true) {
int v = readInt(prompt);
if (v >= lo && v <= hi) return v;
std::cout << "Enter a number between " << lo << " and " << hi << ".\n";
}
}
int choice = readIntInRange("Choose 1-5: ", 1, 5);
This is the backbone of any menu-driven program.
Strict Validation: Rejecting “12abc”
cin >> n on 12abc succeeds and gives you 12, leaving abc behind. If you want the whole input to be a valid number, read the line and parse it:
#include <sstream>
bool parseInt(const std::string& text, int& out) {
std::istringstream ss(text);
char leftover;
return (ss >> out) && !(ss >> leftover); // nothing may remain
}
int main() {
std::string line;
int n;
while (true) {
std::cout << "Enter a number: ";
std::getline(std::cin, line);
if (parseInt(line, n)) break;
std::cout << "Whole numbers only.\n";
}
std::cout << n << '\n';
}
Reading with getline throughout also sidesteps the mixing problem entirely — you never have a stray newline sitting in the buffer.
Checking the Stream State
| Call | True when |
|---|---|
cin.good() | Everything is fine |
cin.fail() | The last read failed (wrong type, or formatting) |
cin.eof() | End of input was reached (Ctrl+D / Ctrl+Z) |
cin.bad() | Unrecoverable stream corruption |
Watch out for end of input: if the user presses Ctrl+D, cin hits EOF and clear() + retry loops forever again. A robust loop checks for it:
if (std::cin.eof()) {
std::cout << "\nInput closed.\n";
return 1;
}
Quick Reference
| Problem | Fix |
|---|---|
| Infinite loop on bad input | cin.clear(); cin.ignore(max, '\n'); |
getline skipped after cin >> | cin.ignore(max, '\n'); before the getline |
12abc accepted as 12 | Read with getline + parse with stringstream |
| Need a valid range | Loop until v >= lo && v <= hi |
| Ctrl+D causes a spin | Check cin.eof() and exit |
Take Your C++ Further
If you want input handling and the rest of the fundamentals explained properly rather than patched together, the C++ Better Explained Ebook covers it in plain English. Just $19.
👉 Get the C++ Better Explained Ebook — $19
Related Articles
- cin.ignore() and Clearing the Input Buffer — the mechanics behind the fix.
- C++ cin and User Input — how extraction works in the first place.
- Using getline() for String Input — reading whole lines safely.
- C++ Menu-Driven Programs — where input validation matters most.
- C++ Error Messages Explained — decoding what the compiler tells you.