Why does C style cast work but reinterpret_cast doesn't?
-
27-06-2021 - |
题
So I have a two char array
unsigned char v[2];
I want to show the value of v[0] as a number from 0 to 255 but
cout << v[0] << endl; //prints some garbage
cout << (void*)v[0] << endl; //prints the right thing but in hex
So I tried
cout << (int)v[0] << endl;
or
printf("%d\n", v[0]);
This shows exactly what I wanted but I don't like it at all. Also what I don't understand at all is why this doesn't work:
cout << reinterpret_cast<int>(v[0]) << endl; //compiler error
解决方案
(In layman's terms) reinterpret_cast
is used to interpret the bits of an object as another type in an implementation-defined manner. You don't want that: you want a conversion (from char to int). Use static_cast
instead.
(All possible uses of reinterpret_cast
are listed in 5.2.10; this is not one of them.)
其他提示
cout << v[0] << endl; // prints some garbage
Not garbage, but the character that the value in v[0] represents.
cout << (void*)v[0] << endl;
This "converts" the value in v[0]
to a pointer (undefined behavior, since it wasn't a pointer to begin with), and prints the value of that pointer as a hex value.
cout << (int)v[0] << endl;
This converts the value in v[0]
to an int (well defined; just promote to int) and displays the value.
reinterpret_cast
, as others have mentioned, has some constraints on what it can do. The right answer is to use static_cast<int>
:
cout << static_cast<int>(v[0]) << endl;
reinterpret_cast
: "...allows any integral type to be converted into any pointer type and vice versa." Use this only to convert things that could be pointers into the right pointer type (an unsigned char clearly cannot be a pointer in anything other than an 8-bit system).static_cast
: Use this when you want to cast a type into another type (like a float to int, or unsigned char to int).
cout
tries to print an unsigned char as an ASCII character code, so converting to int via static_cast<int>(v[0])
is the right thing to do.
The way you are using reinterpret_cast, you are trying to read a char as an int, and they are not the same size.
reinterpret_cast
converts values of pointer types to other pointer type values or integer values and vice versa, in order to allow a dereference of the destination pointer value. You however try to convert one integer type value to another integer type value. That's not going to work. reinterpret_cast
supports the casting of reference types, which is equivalent to the respective pointer value cast with a follow-up dereference. So it appears you wanted to do
// NOTE: THIS IS UNDEFINED BEHAVIOR
cout << reinterpret_cast<int&>(v[0]) << endl;
This is well-formed, but reinterprets the unsigned char as an integer object, which isn't guaranteed to work (possible issues range from invalid alignment of the storage to incompatible sizes - 1 byte vs 4 bytes). You can solve these cases by doing the (int)v[0]
cast, I think that's perfectly fine. You can also say +v[0]
, which promotes the value to (signed or unsigned) int
automatically.
static_cast
will, as others have noted, do the right thing. It is weaker than a C-style cast, so it is less likely to be damaging and cause undefined behavior.
reinterpret_cast
is also weaker than a C-style cast, but it is not strictly stronger than static_cast
; there are things static_cast
can do that reinterpret_cast
cannot. You have run into one of them.
reinterpret_cast
is intended to permit you to take values of one type, store them as values of another, then get back the original type. You can do this to convert beteen integer and pointer types that are compatible. You can use this to convert from one pointer type to another and back again.
In general, a reinterpret_cast
converts your to a "holding" value, and that holding value is implementation defined at best, and often using it is undefined behavior.
So you want to avoid using it unless you really really mean it.
Converting a char
to an int
is not a matter of reinterpreting usually.
static_cast<int>(some_char)
will upgrade the char
to an int
and get you the appropriate value.
However, in certain circumstances I find static_cast
too powerful. It can do unsafe pointer conversions, and counts as an explicit conversion.
Both of these can disguise bugs.
You can write a strictly weaker cast as follows:
template<class T, class U>
T implicit_cast( U&& u ) { return std::forward<U>(u); }
now you can
std::cout << implicit_cast<int>(v[0]) << std::endl;
and you get behavior very much like calling a function taking an int
with a char
argument, which I believe is what you want.
What is going wrong here? Well, std::cout
is a stream, and streams pay attention to the type passed. char
types are printed as if they are `'a`` style characters, not as if they are small integers.
In order to treat a char
as a small integer, you have to convert it to another integer type.
The same is true of char*
; they are treated as pointers to null-terminated buffers of char
, like "hello world"
, and not like other pointers.