1

Given this code:

#include <cstdio>
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::string;

int main() {
        int a;
        string b;

        cin >> a;
        cin >> b;

        return 0;
}

I tried compiling it with g++ and running it. When assigning a char to a, at the first cin, the following instruction seems to be skipped.

Even if add two getchar() instructions between the last two lines, only the second getchar() seems to be executed. Can somebody accurately explain what's happening at low level, which seemingly results in an apparent non execution of those lines?

EDIT:

Using this debug code:

#include <cstdio>
#include <iostream>
#include <string>
using std::cin;
using std::cout;
using std::endl;
using std::string;

int main() {
        int a;
        string b;

        cin >> a;
        cin >> b;
        cout << "a is "<< a << endl;
        cout << "b is "<< b << endl;
        getchar();

        return 0;
}

INPUT 1test

OUTPUT a is 1 b is test * No getchar executed *

INPUT 1 test

OUTPUT a is 1 b is test

INPUT ttest

OUTPUT a is 0 b is

INPUT t

// Skips the second cin

OUTPUT a is 0 b is

NOTE: getchar() was not executed even once.

Stencil
  • 1,833
  • 1
  • 17
  • 19
  • i tried the code, and both the cins seem to be working for me.How are you running the program? Which OS are you running it on? Are u using a terminal ? – woodstok May 23 '12 at 09:20
  • I'm running it on Mac OS X through Terminal. – Stencil May 23 '12 at 09:36
  • 1
    If you enter a `char` where an `int` is expected it puts the `cin` stream into an error state. Every operation with `cin` is a noop (no-operation) until you reset the state to good. – JohnPS May 23 '12 at 09:37
  • That's the answer I was looking for! What do you mean by state reset? It is related to the non execution of getchar()? – Stencil May 23 '12 at 09:39
  • 1
    Sorry, you need to call `cin.clear()`. This will clear the `failbit` (and `badbit`), so that you can read more input. You probably want to check if the read failed first. – JohnPS May 23 '12 at 09:45

3 Answers3

2

You probably hit enter after the first character. You don't have any code to consume that enter, so you get an empty string. Your code doesn't expect any separator between the two inputs, so don't enter any.

David Schwartz
  • 179,497
  • 17
  • 214
  • 278
1

Two things, judging from your output. The first is when you enter "ttest", the cin >> a; fails. This puts cin in an error state, where it will remain until the error is cleared. And as long as it is in an error state, all other operations are no-ops. You really need to test the results of the input before trying to use the values:

std::cin >> a;
if ( !cin ) {
    std::cerr << "That wasn't an integer" << std::endl;
    std::cin.clear();
}
std::cin >> b;
if ( !cin ) {
    std::cerr << "Where was the string" << std::endl;
    std::cin.clear();
}

(And don't use a non-initialized variable, like a, until it has been successfully input.)

The second is that the >> operator only extracts the characters necessary for its target: >> to an int will stop at the first non-numeric character, and >> to a std::string at the first white space (in both cases, after having skipped leading white space). This means that after something like "1test\n", there will still be a '\n' in the buffer. And while it's generally a bad idea to mix FILE* (like getchar()) and iostream, if they're correctly synchronized, getchar() will read this '\n' immediately and return.

If you're reading line oriented input, the best solution is to use getline(), and then put the line into a std::istringstream to parse it. So your code might end up looking like:

std::string line:
std::getline(std::cin, line);
if ( ! std::cin ) {
    //  Something unexpected went wrong...
    std::cin.clear();
} else {
    std::istringstream l( line );
    l >> a >> b;
    if ( !l ) {
        //  Format error in input...
    } else {
        //  use your data here...
    }
}
std::cin.get();  //  Wait for one more character...
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Chosen this as the answer since it is the most exhaustive. JohnPS's answer is really good, too! – Stencil May 23 '12 at 12:48
  • @Stencil Except that JohnPS's answer is wrong. Using `cin.good()` will not work in general; this function has, in fact, no known use. – James Kanze May 23 '12 at 12:54
  • That's true. But I acknowledge the fact he mentioned the nop operation replacing the normally expected code of cin after a failed read and cin.clear() to clear the reading buffer. And by the way, thank you for the long explanation (I was pleased that you explained the getchar() matter). – Stencil May 23 '12 at 13:02
0

When you enter a char and wanted to read an int, the cin stream will have its failbit set. Calling cin.fail() will return true if the operation failed (ether failbit or badbit is set). You can act accordingly from there.

Note that cin can be converted to bool for testing, and according to the standard this boolean value is the same as !cin.fail(), and !cin is the same as cin.fail(). Both of the following are the same, but some people may consider the second more readable.

if(!cin) {
  // cin is in failed state
}

if(cin.fail()) {
  // cin is in failed state
}

Once cin is in a fail state, any read operation with cin will be a no-op until you reset the stream to a good state. You can do that by calling cin.clear().

But you must also realize that the offending characters are still in the stream. That is, the non-integer character that caused the failed state will still be in the stream. How you handle this depending on how you want to recover from the error. One possibility is to call

cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');

This will clear out the current line. Then you can prompt for the input again.

JohnPS
  • 2,518
  • 19
  • 17
  • -1 for suggesting using `cin.good()`. This is never the correct solution, because it may return false even if the results are correct. – James Kanze May 23 '12 at 10:07
  • @JamesKanze: Yes, I should have said to check `cin.fail()` which is the same as `!cin`. – JohnPS May 24 '12 at 00:09