简体   繁体   中英

Weird behaviour when assigning a char to a int variable

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.

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.

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...

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM