Writing to a signed integer as if it is unsigned in C++

382 views Asked by At

Is reinterpret_cast safe for this, and is it the best way to do this?

For example, in the code below, I have a class called ibytestream, which allows the reading of uint16_ts and int16_ts from it. ibytestream::next is a vector<unsigned char>::iterator.

inline ibytestream& operator>>(ibytestream& stream, uint16_t& data) {
    data = 0;
    data |= *stream.next++;
    data <<= 8;
    data |= *stream.next++;
    return stream;
}

inline ibytestream& operator>>(ibytestream& stream, int16_t& data) {
    return stream >> reinterpret_cast<uint16_t&>(data);
}

I don't want to duplicate the code for converting the bytes to an integer, so I used reinterpret_cast for the signed version to reuse the code from the unsigned version. It works fine on my machine, but will it work in general on other modern machines?

2

There are 2 answers

1
Sergey Kalinichenko On BEST ANSWER

Yes, this is safe.

Three parts of the standard apply to make this determination:

  1. Alignment requirements of signed and unsigned types are the same
  2. Pointer casts between pointers to types with identical alignment requirements are allowed
  3. When casts between glvalues are performed, the cast is valid if the cast between the corresponding pointers is valid.

For each of the standard signed integer types, there exists a corresponding (but different) standard unsigned integer type: unsigned char, unsigned short int, unsigned int, unsigned long int, and unsigned long long int, each of which occupies the same amount of storage and has the same alignment requirements.

An object pointer can be explicitly converted to an object pointer of a different type. Converting a prvalue of type “pointer to T1” to the type “pointer to T2” (where T1 and T2 are object types and where the alignment requirements of T2 are no stricter than those of T1) and back to its original type yields the original pointer value.

A glvalue expression of type T1 can be cast to the type “reference to T2” if an expression of type “pointer to T1” can be explicitly converted to the type “pointer to T2” using a reinterpret_cast. The result refers to the same object as the source glvalue, but with the specified type.

2
Petter Hesselberg On

Yes, that should be perfectly fine. (Moving between ints and byte arrays have potential endian-ness issues, but that's another matter that applies to both signed and unsigned numbers.)

Something completely different: This bit:

data = 0;
data |= *stream.next++;

...can be simplified:

data = *stream.next++;