5

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")

Fresco
  • 289
  • 3
  • 13
  • This question is different as it does not ask for high level stuff. Just keep it real, and remember that objects are just plain data structured in memory. – Fresco Dec 08 '14 at 14:29
  • 4
    What question is still unanswered after reading the duplicate? – deviantfan Dec 08 '14 at 14:32
  • The duplicate question does indeed answer most of my question but does not offer a fair explanation as to what happens behind the scenes. I'll edit the question to add more details. – Fresco Dec 08 '14 at 14:46
  • Note that casting a `uint32_t*` to point into a `char[]` array object is strict-aliasing undefined behaviour in C and C++. (Unless you're using MSVC which defines the behaviour of aliasing for all kinds of pointers.) – Peter Cordes Nov 28 '19 at 23:29
  • The duplicate in question was [When should static\_cast, dynamic\_cast, const\_cast and reinterpret\_cast be used?](//stackoverflow.com/q/332030) - this has since been reopened. – Peter Cordes Nov 29 '19 at 02:03

4 Answers4

8

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.

aschepler
  • 70,891
  • 9
  • 107
  • 161
0

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.

Tom Tanner
  • 9,244
  • 3
  • 33
  • 61
  • Consider a program that asks you for a serial given a username. In what way would be the serial the reinterpret_cast of the username be dangerous ? It's an algorithm, and it works, and as far as the general population knows, there is a complex high level serial generation in the process. Why would you say that reinterpret cast is dangerous, in a low level way of course ? – Fresco Dec 08 '14 at 15:53
  • it's dangerous because if you reinterpret_cast say a char * to something that requires 32 bit alignment you can get random and nasty alignment errors. the compiler can't check if what you're doing is safe – Tom Tanner Dec 08 '14 at 16:03
  • What makes this algorithm special is exactly the fact that it would make no sense in a high level language which means that the compiler interpretation as to which type some data at some address is stands in the way of the implementation of the algorithm itself that's why I use reinterpret_cast, so I can work ( and treat ) an array of 4 chars as a uint32_t or a uint32_t as an int32_t . But anyways, can you provide an example of an alignment error ? – Fresco Dec 08 '14 at 16:27
  • `char c; char a[4]; ;wibble(&a[0]); /*...*/ void wibble(void *a) { int b = *reinterpret_cast(a);}` would compile and likely crash horribly (on an architecture that required alignment). Also the evidence is that one of our compilers in debug mode will align things on the worst possible boundary it can find and you wouldn't need the declaration of `c` in there. – Tom Tanner Dec 08 '14 at 17:04
  • I don't understand what's wrong with the code you provided, assuming there is something wrong with it. It simply creates a 4 byte array ( i.e. char a[4] ) then calls a function which creates another 4 bytes array ( i.e. int b ) then stores the 4 bytes of a in b. The code did compile successfully on my machine giving no errors; it also ran successfully without crashing. – Fresco Dec 08 '14 at 17:30
  • because the 4 byte array doesn't have to be aligned on a 32 bit boundary. this will cause program crashes on certain architectures when you try to dereference it as an integer – Tom Tanner Dec 08 '14 at 17:42
0

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

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • "At low level, all casting have same effect." That is wrong! Consider: { float fval = 7; int ival = (int)fval; cout << ival } fval is represented in memory by: 0x40e00000 while ival by: 0x00000004 therefore the casting took the integer part of fval and stored into ival. If all casting would have the same effect ival would be: 0x40e00000 ( 1088421888 )dec – Fresco Dec 08 '14 at 16:10
  • @Fresco I mean that a C style cast, a reinterpret cast or a static cast would all have same effect. But of course casting numeric values and casting pointers does different things. – Serge Ballesta Dec 08 '14 at 16:30
  • @Fresco : I tested it and `reinterpret_cast(7.0)` gives a compilation error, but `static_cast(7.1)` gives `7` as does `(int) 7.1`. – Serge Ballesta Dec 08 '14 at 22:22
-2

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.

i486
  • 6,491
  • 4
  • 24
  • 41