1

A tiny piece of code drives me crazy but hopefully you can prevent me from jumping out of the window. Look here:

#include <iostream>
#include <cstdint>

int main()
{
    int8_t i = 65;
    int8_t j;

    std::cout << "i = " << i << std::endl; // the 'A' is ok, same as uchar

    std::cout << "Now type in a value for j (use 65 again): " << std::endl;
    std::cin >> j;
    std::cout << "j = " << j << std::endl;

    if (i != j) 
        std::cout << "What is going on here?????" << std::endl;
    else 
        std::cout << "Everything ok." << std::endl;

    return 0;
}

If I use int instead of int8_t everything ok. I need this as 8-bit unsigned integers, not bigger. And btw. with unsigned char it's the same behaviour - of course - as with int8_t.

Anyone with a hint?

Colonel Thirty Two
  • 23,953
  • 8
  • 45
  • 85
Excalibur
  • 286
  • 3
  • 11

5 Answers5

8

int8_t is a typedef for an integer type with the required characteristics: pure 2's-complement representation, no padding bits, size of exactly 8 bits.

For most (perhaps all) compilers, that means it's going to be a typedef for signed char.(Because of a quirk in the definition of the term signed integer type, it cannot be a typedef for plain char, even if char happens to be signed).

The >> operator treats character types specially. Reading a character reads a single input character, not sequence of characters representing some integer value in decimal. So if the next input character is '0', the value read will be the character value '0', which is probably 48.

Since a typedef creates an alias for an existing type, not a new distinct type, there's no way for the >> operator to know that you want to treat int8_t as an integer type rather than as a character type.

The problem is that in most implementations there is no 8-bit integer type that's not a character type.

The only workaround is to read into an int variable and then convert to int8_t (with range checks if you need them).

Incidentally, int8_t is a signed type; the corresponding unsigned type is uint8_t, which has a range of 0..255.

(One more consideration: if CHAR_BIT > 8, which is permitted by the standard, then neither int8_t nor uint8_t will be defined at all.)

Keith Thompson
  • 254,901
  • 44
  • 429
  • 631
  • It would be nicer, I think, if the `_t` types weren't allowed to `typedef` to `char` (but `unsigned` and `signed` `char` were fine). It would stop this from being implementation-defined. – Joseph Mansfield Jul 07 '14 at 19:13
  • @Keith: Thank you for your answer. I will read an int k and cast it: j=(int8_t)k. – Excalibur Jul 07 '14 at 19:14
  • @JosephMansfield: That wouldn't help; `cin >> ...` treats `unsigned char` and `signed char` as character types. – Keith Thompson Jul 07 '14 at 19:15
  • @Excalibur: There's no need for a cast. Just assign it; the conversion is done implicitly. – Keith Thompson Jul 07 '14 at 19:15
  • @KeithThompson Oh. Well I think that sucks too. `char` is a distinct type entirely because it is supposed to be treated as a special character type. – Joseph Mansfield Jul 07 '14 at 19:16
  • @Excalibur: Think about what you want to do if the input is, say, `1000`, which doesn't fit in an `int8_t` or `uint8_t`. – Keith Thompson Jul 07 '14 at 19:22
  • @JosephMansfield: A substantial body of code needs an unsigned type that is guaranteed to be exactly eight bits with no padding and has the same exemption from aliasing rules as `unsigned char`. Since `uint8_t` is the only standard-defined type which, if it exists, is guaranteed to be an 8-bit unsigned value with no padding, and since any implementation which would be allowed to define that type would be able to make it use the same aliasing rules as `unsigned char`, a lot of code uses `uint8_t` when it needs a type having both characteristics. – supercat Jan 09 '17 at 19:52
4

int8_t and uint8_t are almost certainly character types (Are int8_t and uint8_t intended to behave like a character?) so std::cin >> j will read a single character from stdin and interpret it as a character, not as a number.

Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
0

int8_t is likely the same as char, which means cin >> j will simply read a single character ('6') from input and store it in j.

jwodder
  • 54,758
  • 12
  • 108
  • 124
0

int8_t is defined as a typedef name for signed char. So operator >> used with an object of type int8_t behaves the same way as it would be used for an object of type signed char

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • It could be a `typedef` for either `signed char` or plain `char`. – Keith Thompson Jul 07 '14 at 19:16
  • @Keith Thompson No it may not because how type char will behave depends on compiler options. Some compilers by default implement type char as unsigned char. – Vlad from Moscow Jul 07 '14 at 19:31
  • Defining `int8_t` as plain `char` is perfectly legal *if* plain `char` is signed. If the signedness of plain `char` depends on a compiler option, the definition of `int8_t` could be controlled by an `#if` test on, for example, `CHAR_MAX`. It's certainly easier to define it as `signed char`, but implementations aren't required to be sensible. – Keith Thompson Jul 07 '14 at 19:44
  • @Keith Thompson It is a wrong statement. char is not an unsigned integer type while uint8_t shall be defined as unsigned integer type. – Vlad from Moscow Jul 07 '14 at 19:53
  • You're right. `char` is an *integer type*, and it's either signed or unsigned, but it's neither a *signed integer type* nor an *unsigned integer type* (a quirk of the way those terms are defined in the C and C++ standards). Conceivably `int8_t` could be a typedef for an *extended signed integer type* (for example, an implementation could define an 8-bit non-character type for just the problem raised in this question). And if `CHAR_BIT > 8`, then `int8_t` will not be defined at all. – Keith Thompson Jul 07 '14 at 20:00
0

The _t types aren't first class types, they are typedef aliases the observe certain constraints, e.g. int8_t is a type that can store a signed, 8-bit value.

On most systems, this will mean they are typedefd to char. And because they are a typedef and not a first-class type, you are invoking cin.operator<<(char) and cin.operator>>(char).

When you input "65", cin.operator>>(char) consumes the '6' and places it's ascii value, 54, into variable j.

To work around this you'll need to use a different type, possibly the easiest method being just to use a larger integer type and apply constraints and then cast down:

int8_t fetchInt8(const char* prompt) {
    int in = 0;
    for ( ; ; ) { // endless loop.
        std::cout << prompt << ": ";
        std::cin >> in;
        if (in >= std::numeric_limits<int8_t>::min()
              && in <= std::numeric_limits<int8_t>::max()) {
            std::cout << "You entered: " << in << '\n';
            // exit the loop
            break;
        }
        std::cerr << "Error: Invalid number for an int8\n";
    }

    return static_cast<int8_t>(in);
}

Note that int8_t is signed, which means it stores -128 thru +127. If you want only positive values, use the uint8_t type.

kfsone
  • 23,617
  • 2
  • 42
  • 74