Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Reinterpret_cast vs. C-style cast

Tags:

c++

casting

I hear that reinterpret_cast is implementation defined, but I don't know what this really means. Can you provide an example of how it can go wrong, and it goes wrong, is it better to use C-Style cast?

like image 236
user103214 Avatar asked Oct 20 '11 05:10

user103214


People also ask

What is C-style cast?

C-style casts can be used to convert any type into any other type, potentially with unsafe results (such as casting an integer into a pointer type). (<type>)<value> This example casts an int to a double for the purpose of avoiding truncation due to integer division: double result = (double)4/5; Popular pages.

When should I use reinterpret_cast?

Purpose for using reinterpret_cast It is used when we want to work with bits. If we use this type of cast then it becomes a non-portable product. So, it is suggested not to use this concept unless required. It is only used to typecast any pointer to its original type.

Why is static_cast better than C-style cast?

In short: static_cast<>() gives you a compile time checking ability, C-Style cast doesn't. static_cast<>() is more readable and can be spotted easily anywhere inside a C++ source code, C_Style cast is'nt. Intentions are conveyed much better using C++ casts.


2 Answers

The C-style cast isn't better.

It simply tries the various C++-style casts in order, until it finds one that works. That means that when it acts like a reinterpret_cast, it has the exact same problems as a reinterpret_cast. But in addition, it has these problems:

  • It can do many different things, and it's not always clear from reading the code which type of cast will be invoked (it might behave like a reinterpret_cast, a const_cast or a static_cast, and those do very different things)
  • Consequently, changing the surrounding code might change the behaviour of the cast
  • It's hard to find when reading or searching the code - reinterpret_cast is easy to find, which is good, because casts are ugly and should be paid attention to when used. Conversely, a C-style cast (as in (int)42.0) is much harder to find reliably by searching

To answer the other part of your question, yes, reinterpret_cast is implementation-defined. This means that when you use it to convert from, say, an int* to a float*, then you have no guarantee that the resulting pointer will point to the same address. That part is implementation-defined. But if you take the resulting float* and reinterpret_cast it back into an int*, then you will get the original pointer. That part is guaranteed.

But again, remember that this is true whether you use reinterpret_cast or a C-style cast:

int i; int* p0 = &i;  float* p1 = (float*)p0; // implementation-defined result float* p2 = reinterpret_cast<float*>(p0); // implementation-defined result  int* p3 = (int*)p1; // guaranteed that p3 == p0 int* p4 = (int*)p2; // guaranteed that p4 == p0 int* p5 = reinterpret_cast<int*>(p1); // guaranteed that p5 == p0 int* p6 = reinterpret_cast<int*>(p2); // guaranteed that p6 == p0 
like image 65
jalf Avatar answered Oct 09 '22 22:10

jalf


It is implementation defined in a sense that standard doesn't (almost) prescribe how different types values should look like on a bit level, how address space should be structured and so on. So it's really a very platform specific for conversions like:

double d; int &i = reinterpret_cast<int&>(d); 

However as standard says

It is intended to be unsurprising to those who know the addressing structure of the underlying machine.

So if you know what you do and how it all looks like on a low-level nothing can go wrong.

The C-style cast is somewhat similar in a sense that it can perform reinterpret_cast, but it also "tries" static_cast first and it can cast away cv qualification (while static_cast and reinterpret_cast can't) and perform conversions disregarding access control (see 5.4/4 in C++11 standard). E.g.:

#include <iostream>  using namespace std;  class A { int x; }; class B { int y; };  class C : A, B { int z; };  int main() {   C c;    // just type pun the pointer to c, pointer value will remain the same   // only it's type is different.   B *b1 = reinterpret_cast<B *>(&c);    // perform the conversion with a semantic of static_cast<B*>(&c), disregarding   // that B is an unaccessible base of C, resulting pointer will point   // to the B sub-object in c.   B *b2 = (B*)(&c);    cout << "reinterpret_cast:\t" << b1 << "\n";   cout << "C-style cast:\t\t" << b2 << "\n";   cout << "no cast:\t\t" << &c << "\n"; } 

and here is an output from ideone:

 reinterpret_cast:  0xbfd84e78 C-style cast:      0xbfd84e7c no cast:           0xbfd84e78 

note that value produced by reinterpret_cast is exactly the same as an address of 'c', while C-style cast resulted in a correctly offset pointer.

like image 24
Konstantin Oznobihin Avatar answered Oct 09 '22 22:10

Konstantin Oznobihin