(int16_t)uval
converts the uint16_t
value to a int16_t
value. For 1
this works as expected, for 0xffff
it is implementation-defined behavior because 0xffff
does not fit in the bounds of int16_t
. (https://en.cppreference.com/w/cpp/language/implicit_conversion#Integral_conversions). Since C++20, it is defined such that it produces the expected value (see below).
*(int16_t*)&uval
first casts the uint16_t*
pointer to a int16_t*
pointer, and then dereferences it. With the C-style pointer cast, the expression is equivalent to *reinterpret_cast<int16_t*>(&uval)
. (https://en.cppreference.com/w/cpp/language/explicit_cast). static_cast
is not possible because uint16_t
and int16_t
are different types.
Because they are also not "similar types", dereferencing the resulting int16_t*
pointer is also undefined behavior (https://en.cppreference.com/w/cpp/language/reinterpret_cast#Type_aliasing).
Because it is undefined behavior the expressions could theoretically result in anything, but with a normal compiler (without optimizations), with the first expression it would attempt to convert the uint16_t
into a int16_t
, whereas with the second expression, it would attempt to access the raw uint16_t
value as if it were a int16_t
, without modifying it.
This results in the same value, because of the way signed integer values are stored in two's-complement: Positive values have the same bitwise expression in signed and unsigned types. 0x0001
means 1
for both. But 0xffff
(all-one bytes) means 65535
for an uint16_t
, and -1
for an int16_t
.
So (int16_t)uval
(and also (uint16_t)sval
) does not need to modify the bitwise value at all, because all values that are in the range of both int16_t
and uint16_t
are expressed the same way for both. And for values outside the range, it is undefined behavior, so the compiler simply doesn't modify the bitwise value in that case either.
The only way to get the effect of the second expression (accessing raw bitwise data as if it were another type) without undefined behavior would be to use memcpy
: int16_t sval; std::memcpy(&sval, &uval, 2);
.