c++ difference between reinterpret cast and c style cast

3.9k views Asked by At

Code:

char keyStr[50]={ 0x5F, 0x80 /* bla bla */ };
uint32_t* reCast  = reinterpret_cast< uint32_t* >( &keyStr[29] );
uint32_t* reCast2 = ( uint32_t* )&keyStr[29];
if( reCast == reCast2 ){
    cout << "Same Thing!";
}

Output:

Same Thing!

I wonder what's the difference between the two casting methods. Also if you could specify ( with examples ) difference between static_cast, dynamic_cast, and other types of casting you know ( i.e. while staying as low level and as close to assembly language as possible ).

static_cast
dynamic_cast
const_cast
reinterpret_cast
C-style cast (type)value
Function-style cast type(value)

Thanks.

Please read the P.S. I know from the example above that reinterpret_cast assigns to the int pointer the address of keyStr[29] In assembly that would translate into:

lea eax, [keyStr+1D]
mov [reCast], eax

So in other words reinterpret_cast, in a low level prospective, is not dangerous at all as it does not modify the original data.

I wanted to know how the other casting methods behave in a low level way. So, for example, an object, in a low level way, is just a variable which holds an address. And the type if that object is how the compiler then interprets that address and how it offsets it.( this is exactly what I'm not interested in, in assembly, i could care less if that variable holds a value, a pointer or an object ( i.e. another pointer ) ). Another thing that could be just the same, is the difference between int and int* or unsigned int and int; all 4 declarations generate the same assembly instruction. ( push value ) or (sub esp-(length of int) && mov esp, value) I hope this clarifies the question and why I tagged it "low-level-code" and "assembly"

P.S. In this program I'm trying to create I don't care for non portability or other high level stuff. I'm trying to be as low level as possible and as close to assembly language as possible. That means that, for this program, memory is just memory ( i.e. 0 and 1 bits ) and types are not important ( e.g. I don't care if mem address: 0x123 is an "int" type or "float" type, it's just "data")

4

There are 4 answers

0
aschepler On BEST ANSWER

reinterpret_cast and const_cast are ways of getting around the C++ type system. As you noted for reinterpret_cast, this usually translates to little or no assembly code.

static_cast mostly respects the C++ type system. It could convert a number from one type to another, or call a constructor, or call a conversion function. Or for a derived-to-base conversion, it might involve adding byte offsets and/or lookups into a vtable. static_cast can also bend the type system's rules by "downcasting" a pointer or reference from a non-virtual base type to a derived type, possibly subtracting a byte offset.

And then there are pointers-to-member. They're probably beside the point here, but static_cast does things to them more or less analogous to class pointer conversions.

dynamic_cast respects the C++ type system even more strictly. In its useful form, it checks at runtime whether or not a pointer/reference actually points/refers to an object of the specified type. It typically calls a magic library function under the covers.

A function-style cast with one argument has exactly the same effect as a C-style cast. (With more than one argument, a function-style cast must be a temporary initialization using a class constructor.) A C-style cast does the first thing that makes sense out of the following list:

  • a const_cast
  • a static_cast
  • a static_cast and then a const_cast
  • a reinterpret_cast, or
  • a reinterpret_cast and then a const_cast

One exception: C-style casts can ignore private and protected inheritance relations between classes, pretending they have a public inheritance relation instead.

C-style casts are usually not preferred in C++ because it's less specific about what you want to happen.

1
i486 On

The difference is that with C-style cast in C++ file in some cases you will get error and cannot compile. reinterpret_cast solves such cases. Something like - you tell to compiler: "I know it is incompatible casting, but let assume it is ok". C++ is far more restricted than C for such things like casting.

6
Tom Tanner On

In what way do you mean "not dangerous"? reinterpret_cast is incredibly dangerous. It tells the compiler it is safe to ignore what it thinks it knows about the value.

It's not as dangerous as a c-style cast which throws away the const/volatileness of the value in question as well as any information about what it is pointing to.

Understanding these operations in assembly language is a bit pointless. They aren't assembly language constructs. They're C++ language constructs, that work something as follows:

static_cast - Effectively this converts an object from one type to another. Note this can change the value (static_cast<float>(1) doesn't have the same bit pattern as 1 for instance).

dynamic_cast - if this object can be considered to be another type through inheritance, then treat it as such, otherwise render it as zero. This won't change the value of a pointer but it does safely change the compilers view of it.

const_cast - throw away const (or volatile) qualifiers, which is not often a good idea as it allows you to destroy data the client thought was safe.

reinterpret_cast - treat the bit pattern as meaning something different to what the compiler thought it did. Usually used for pointers and hopefully rarely. reinterpret_casting an int into a float is unlikely to be a good idea, but it will keep the same bit pattern.

c-style-cast - Take the bit pattern, forget completely what you know about it, and treat it as something else. A dangerous and almost invisible combination of static_cast, reinterpret_cast and const_cast. It's not considered a good idea in C++ code because it's hard to spot in a review, and because it is not specific about what is happening.

3
Serge Ballesta On

In your example there are no differences between the C style cast and the reinterpret_cast, because you are casting between unrelated pointers, and there is no constness. If you have had a const on one part, reinterpret_cast would have choked when C style cast would have done the const_cast under the hood.

The danger of the reinterpret_cast (or of the C style cast) is precisely that it allows casting between unrelated objects. In your example, you have a high risk that when you dereference reCast (or reCast2) you get an error because you try to access a misaligned integer.

At low level, all casting have same effect (if they are valid) : they will all give the value or address. The main difference is :

  • a C style cast will (almost) always be allowed at compile time - I do not know examples where it will give a compile error but it might be compiler dependant
  • a reinterpret_cast will be allowed in same cases provided there is no constness change
  • a const_cast can only change constness
  • a static_cast will only be allowed (at compile time) between related types

All this cast were added in C++ to avoid the catch all mode of C style cast and allow for some compile and run time checks. - a dynamic_cast will only be allowed at compile time between related types and the compiler will insert code to control validity at run time