620

I am little confused with the applicability of reinterpret_cast vs static_cast. From what I have read the general rules are to use static cast when the types can be interpreted at compile time hence the word static. This is the cast the C++ compiler uses internally for implicit casts also.

reinterpret_casts are applicable in two scenarios:

  • convert integer types to pointer types and vice versa
  • convert one pointer type to another. The general idea I get is this is unportable and should be avoided.

Where I am a little confused is one usage which I need, I am calling C++ from C and the C code needs to hold on to the C++ object so basically it holds a void*. What cast should be used to convert between the void * and the Class type?

I have seen usage of both static_cast and reinterpret_cast? Though from what I have been reading it appears static is better as the cast can happen at compile time? Though it says to use reinterpret_cast to convert from one pointer type to another?

2
  • 28
    reinterpret_cast does not happen at run time. They are both compile-time statements. From en.cppreference.com/w/cpp/language/reinterpret_cast: "Unlike static_cast, but like const_cast, the reinterpret_cast expression does not compile to any CPU instructions. It is purely a compiler directive which instructs the compiler to treat the sequence of bits (object representation) of expression as if it had the type new_type." Mar 3, 2017 at 18:44
  • @HeretoLearn, is it possible to add the relevant code pieces from the *.c and *.cpp file? I think it can improve the exposition of the question. Nov 16, 2017 at 7:48

11 Answers 11

571

The C++ standard guarantees the following:

static_casting a pointer to and from void* preserves the address. That is, in the following, a, b and c all point to the same address:

int* a = new int();
void* b = static_cast<void*>(a);
int* c = static_cast<int*>(b);

reinterpret_cast only guarantees that if you cast a pointer to a different type, and then reinterpret_cast it back to the original type, you get the original value. So in the following:

int* a = new int();
void* b = reinterpret_cast<void*>(a);
int* c = reinterpret_cast<int*>(b);

a and c contain the same value, but the value of b is unspecified. (in practice it will typically contain the same address as a and c, but that's not specified in the standard, and it may not be true on machines with more complex memory systems.)

For casting to and from void*, static_cast should be preferred.

13
  • 18
    I like the fact that 'b' is undefined. It stops you doing silly things with it. If you cast something to another pointer type you are asking for problems and the fact that you can not depend on it makes you more careful. If you had used static_cast<> above what use is the 'b' anyway? Feb 21, 2009 at 21:27
  • 5
    I thought that reinterpret_cast<> guaranteed the same bit pattern. (which is not the same as a valid pointer to another type). Feb 21, 2009 at 21:37
  • 49
    the value of b is not unspecified anymore in C++11 when using reinterpret_cast. And in C++03 a cast of int* to void* was forbidden to be done with reinterpret_cast (although compilers did not implement that and it was impractical, hence was changed for C++11). Oct 28, 2011 at 23:20
  • 126
    This does not actually answer the question of "when to use reinterpret_cast".
    – einpoklum
    Apr 15, 2016 at 17:56
  • 11
    @LokiAstari I think unspecified does not stop you from doing silly things. It only stop you when you remember it's unspecified. Huge difference. Personally I don't like unspecified. Too much to remember.
    – Helin Wang
    Jan 30, 2017 at 15:54
218

One case when reinterpret_cast is necessary is when interfacing with opaque data types. This occurs frequently in vendor APIs over which the programmer has no control. Here's a contrived example where a vendor provides an API for storing and retrieving arbitrary global data:

// vendor.hpp
typedef struct _Opaque * VendorGlobalUserData;
void VendorSetUserData(VendorGlobalUserData p);
VendorGlobalUserData VendorGetUserData();

To use this API, the programmer must cast their data to VendorGlobalUserData and back again. static_cast won't work, one must use reinterpret_cast:

// main.cpp
#include "vendor.hpp"
#include <iostream>
using namespace std;

struct MyUserData {
    MyUserData() : m(42) {}
    int m;
};

int main() {
    MyUserData u;

        // store global data
    VendorGlobalUserData d1;
//  d1 = &u;                                          // compile error
//  d1 = static_cast<VendorGlobalUserData>(&u);       // compile error
    d1 = reinterpret_cast<VendorGlobalUserData>(&u);  // ok
    VendorSetUserData(d1);

        // do other stuff...

        // retrieve global data
    VendorGlobalUserData d2 = VendorGetUserData();
    MyUserData * p = 0;
//  p = d2;                                           // compile error
//  p = static_cast<MyUserData *>(d2);                // compile error
    p = reinterpret_cast<MyUserData *>(d2);           // ok

    if (p) { cout << p->m << endl; }
    return 0;
}

Below is a contrived implementation of the sample API:

// vendor.cpp
static VendorGlobalUserData g = 0;
void VendorSetUserData(VendorGlobalUserData p) { g = p; }
VendorGlobalUserData VendorGetUserData() { return g; }
11
  • 11
    Yep, that's about the only meaningful use of reinterpret_cast I can think of.
    – jalf
    Feb 21, 2009 at 21:36
  • 13
    This may be a late question, but why doesn't the vendor API use void* for that?
    – Xeo
    Oct 29, 2011 at 1:09
  • 29
    @Xeo They don't use void * because then they lose (some) type-checking at compile time.
    – jesup
    Aug 14, 2014 at 6:17
  • 4
    A practical use case of "opaque" data types is when you want to expose an API to C but write the implementation in C++. ICU is an example of a library that does this in several places. For example, in the spoof checker API, you deal with pointers of type USpoofChecker*, where USpoofChecker is an empty struct. However, under the hood, whenever you pass a USpoofChecker*, it undergoes reinterpret_cast to an internal C++ type.
    – sffc
    Mar 23, 2017 at 8:08
  • 2
    @jaskmar You can reinterpret_cast pointers, though. That's the trick here: vendor's code actually never tries to use VendorGlobalUserData's value, it only takes it from the user code and passes it back. And the user code knows exactly what the correct type of the pointer is, so it can reinterprete_cast it back and access the original object, which is defined.
    – yeputons
    Oct 21, 2020 at 12:13
196

The short answer: If you don't know what reinterpret_cast stands for, don't use it. If you will need it in the future, you will know.

Full answer:

Let's consider basic number types.

When you convert for example int(12) to float (12.0f) your processor needs to invoke some calculations as both numbers have different bit representation. This is what static_cast stands for.

On the other hand, when you call reinterpret_cast the CPU does not invoke any calculations. It just treats a set of bits in the memory like if it had another type. So when you convert int* to float* with this keyword, the new value (after pointer dereferecing) has nothing to do with the old value in mathematical meaning (ignoring the fact that it is undefined behavior to read this value).

Be aware that reading or modifying values after reinterprt_cast'ing are very often Undefined Behavior. In most cases, you should use pointer or reference to std::byte (starting from C++17) if you want to achieve the bit representation of some data, it is almost always a legal operation. Other "safe" types are char and unsigned char, but I would say it shouldn't be used for that purpose in modern C++ as std::byte has better semantics.

Example: It is true that reinterpret_cast is not portable because of one reason - byte order (endianness). But this is often surprisingly the best reason to use it. Let's imagine the example: you have to read binary 32bit number from file, and you know it is big endian. Your code has to be generic and has to work properly on big endian (e.g. some ARM) and little endian (e.g. x86) systems. So you have to check the byte order. It is well-known on compile time so you can write constexpr function: You can write a function to achieve this:

/*constexpr*/ bool is_little_endian() {
  std::uint16_t x=0x0001;
  auto p = reinterpret_cast<std::uint8_t*>(&x);
  return *p != 0;
}

Explanation: the binary representation of x in memory could be 0000'0000'0000'0001 (big) or 0000'0001'0000'0000 (little endian). After reinterpret-casting the byte under p pointer could be respectively 0000'0000 or 0000'0001. If you use static-casting, it will always be 0000'0001, no matter what endianness is being used.

EDIT:

In the first version I made example function is_little_endian to be constexpr. It compiles fine on the newest gcc (8.3.0) but the standard says it is illegal. The clang compiler refuses to compile it (which is correct).

14
  • 5
    Nice example! I would replace short for uint16_t and unsigned char for uint8_t to make it less obscure for human.
    – Jan Turoň
    Aug 27, 2018 at 9:41
  • 3
    @JanTuroň true, we can not assume that short takes 16 bits in memory. Corrected. Aug 28, 2018 at 9:30
  • 5
    The example is wrong. reinterpret_cast is not allowed in constexpr functions Mar 2, 2019 at 11:07
  • 1
    First of all, this code is rejected by both latest clang (7.0.0) and gcc (8.2.0). Unfortunately I did not find the limitation in formal language. All I could find was social.msdn.microsoft.com/Forums/vstudio/en-US/… Mar 2, 2019 at 16:03
  • 3
    More specifically, en.cppreference.com/w/cpp/language/constant_expression (item 16) clearly states that reinterpret_cast can't be used in a constant expression. Also look at github.com/cplusplus/draft/blob/master/papers/N3797.pdf (5.19 constant expressions) pages125-126 which explicitly rules out reinterpret_cast. Then 7.1.5 The constexpr specifier item 5 (page 146) *For a non-template, non-defaulted constexpr function ... if no argument values exist such that ... could be an evaluated subexpression of a core constant expression (5.19), the program is ill-formed * Mar 3, 2019 at 9:18
21

The meaning of reinterpret_cast is not defined by the C++ standard. Hence, in theory a reinterpret_cast could crash your program. In practice compilers try to do what you expect, which is to interpret the bits of what you are passing in as if they were the type you are casting to. If you know what the compilers you are going to use do with reinterpret_cast you can use it, but to say that it is portable would be lying.

For the case you describe, and pretty much any case where you might consider reinterpret_cast, you can use static_cast or some other alternative instead. Among other things the standard has this to say about what you can expect of static_cast (§5.2.9):

An rvalue of type “pointer to cv void” can be explicitly converted to a pointer to object type. A value of type pointer to object converted to “pointer to cv void” and back to the original pointer type will have its original value.

So for your use case, it seems fairly clear that the standardization committee intended for you to use static_cast.

7
  • 7
    Not quite crash your program. The standard offers a few guarantees about reinterpret_cast. Just not as many as people often expect.
    – jalf
    Feb 21, 2009 at 16:43
  • 2
    Not if you use it properly. That is, reinterpret_cast from A to B to A is perfectly safe and well-defined. But the value of B is unspecified, and yes, if you rely on that, bad things could happen. But the cast itself is safe enough, as long as you only used it in the way the standard allows. ;)
    – jalf
    Feb 21, 2009 at 19:10
  • 62
    lol, I suspect that reinterpret_crash might indeed crash your program. But reinterpret_cast won't. ;)
    – jalf
    Feb 21, 2009 at 21:35
  • 7
    <irony>I tried it on my compiler, and somehow, it refused to compile reinterpret_crash. No way a compiler bug will stop me from crashing my reinterpreting program. I'll report a bug ASAP!</irony>
    – paercebal
    Jul 11, 2010 at 11:56
  • 20
    @paercebal template<class T, U> T reinterpret_crash(U a) { return *(T*)nullptr; }
    – user142019
    Jan 14, 2012 at 10:41
11

One use of reinterpret_cast is if you want to apply bitwise operations to (IEEE 754) floats. One example of this was the Fast Inverse Square-Root trick:

https://en.wikipedia.org/wiki/Fast_inverse_square_root#Overview_of_the_code

It treats the binary representation of the float as an integer, shifts it right and subtracts it from a constant, thereby halving and negating the exponent. After converting back to a float, it's subjected to a Newton-Raphson iteration to make this approximation more exact:

float Q_rsqrt( float number )
{
    long i;
    float x2, y;
    const float threehalfs = 1.5F;

    x2 = number * 0.5F;
    y  = number;
    i  = * ( long * ) &y;                       // evil floating point bit level hacking
    i  = 0x5f3759df - ( i >> 1 );               // what the deuce? 
    y  = * ( float * ) &i;
    y  = y * ( threehalfs - ( x2 * y * y ) );   // 1st iteration
//  y  = y * ( threehalfs - ( x2 * y * y ) );   // 2nd iteration, this can be removed

    return y;
}

This was originally written in C, so uses C casts, but the analogous C++ cast is the reinterpret_cast.

4
  • 1
    error: invalid cast of an rvalue expression of type 'int64_t {aka long long int}' to type 'double&' reinterpret_cast<double&>((reinterpret_cast<int64_t&>(d) >> 1) + (1L << 61)) - ideone.com/6S4ijc Jul 10, 2016 at 15:52
  • 3
    The standard says that this is undefined behavior: en.cppreference.com/w/cpp/language/reinterpret_cast (under "type aliasing") Mar 3, 2017 at 18:40
  • @CrisLuengo If I substitute all reinterpret_cast with memcpy, is it still UB?
    – sandthorn
    Jul 16, 2018 at 16:37
  • 1
    @sandthorn: This is UB according to the standard, but if it works for your architecture, don't worry about it. This trick is OK, I presume, for any compiler for Intel architectures. It could not work as intended (or even crash) on other architectures -- for example it could be possible that floats and longs are stored in separate memory compartments (not that I know of any such architecture, it's just an argument...). memcpy would definitely make it legal. Jul 16, 2018 at 16:55
6

Here is a variant of Avi Ginsburg's program which clearly illustrates the property of reinterpret_cast mentioned by Chris Luengo, flodin, and cmdLP: that the compiler treats the pointed-to memory location as if it were an object of the new type:

#include <iostream>
#include <string>
#include <iomanip>
using namespace std;

class A
{
public:
    int i;
};

class B : public A
{
public:
    virtual void f() {}
};

int main()
{
    string s;
    B b;
    b.i = 0;
    A* as = static_cast<A*>(&b);
    A* ar = reinterpret_cast<A*>(&b);
    B* c = reinterpret_cast<B*>(ar);
    
    cout << "as->i = " << hex << setfill('0')  << as->i << "\n";
    cout << "ar->i = " << ar->i << "\n";
    cout << "b.i   = " << b.i << "\n";
    cout << "c->i  = " << c->i << "\n";
    cout << "\n";
    cout << "&(as->i) = " << &(as->i) << "\n";
    cout << "&(ar->i) = " << &(ar->i) << "\n";
    cout << "&(b.i) = " << &(b.i) << "\n";
    cout << "&(c->i) = " << &(c->i) << "\n";
    cout << "\n";
    cout << "&b = " << &b << "\n";
    cout << "as = " << as << "\n";
    cout << "ar = " << ar << "\n";
    cout << "c  = " << c  << "\n";
    
    cout << "Press ENTER to exit.\n";
    getline(cin,s);
}

Which results in output like this:

as->i = 0
ar->i = 50ee64
b.i   = 0
c->i  = 0

&(as->i) = 00EFF978
&(ar->i) = 00EFF974
&(b.i)   = 00EFF978
&(c->i)  = 00EFF978

&b = 00EFF974
as = 00EFF978
ar = 00EFF974
c  = 00EFF974
Press ENTER to exit.

It can be seen that the B object is built in memory as B-specific data first, followed by the embedded A object. The static_cast correctly returns the address of the embedded A object, and the pointer created by static_cast correctly gives the value of the data field. The pointer generated by reinterpret_cast treats b's memory location as if it were a plain A object, and so when the pointer tries to get the data field it returns some B-specific data as if it were the contents of this field.

One use of reinterpret_cast is to convert a pointer to an unsigned integer (when pointers and unsigned integers are the same size):

int i; unsigned int u = reinterpret_cast<unsigned int>(&i);

1
  • 2
    Everything here but the last example is undefined behavior; it’s interesting only as a (not reliable) means of illustrating implementation details of the language. Aug 18, 2020 at 14:30
3

You could use reinterprete_cast to check inheritance at compile time.
Look here: Using reinterpret_cast to check inheritance at compile time

0
2

First you have some data in a specific type like int here:

int x = 0x7fffffff://==nan in binary representation

Then you want to access the same variable as an other type like float: You can decide between

float y = reinterpret_cast<float&>(x);

//this could only be used in cpp, looks like a function with template-parameters

or

float y = *(float*)&(x);

//this could be used in c and cpp

BRIEF: it means that the same memory is used as a different type. So you could convert binary representations of floats as int type like above to floats. 0x80000000 is -0 for example (the mantissa and exponent are null but the sign, the msb, is one. This also works for doubles and long doubles.

OPTIMIZE: I think reinterpret_cast would be optimized in many compilers, while the c-casting is made by pointerarithmetic (the value must be copied to the memory, cause pointers couldn't point to cpu- registers).

NOTE: In both cases you should save the casted value in a variable before cast! This macro could help:

#define asvar(x) ({decltype(x) __tmp__ = (x); __tmp__; })
2
  • 1
    It is true that "it means that the same memory is used as a different type" but it is restricted to specific pair of types. In your example reinterpret_cast form int to float& is undefined behaviour. Dec 19, 2019 at 8:16
  • Compilers optimize memcpy to purely register operations when possible; the casts are easy (but are also UB — if the value is used — as pointed out all over this page). Aug 18, 2020 at 14:32
1
template <class outType, class inType>
outType safe_cast(inType pointer)
{
    void* temp = static_cast<void*>(pointer);
    return static_cast<outType>(temp);
}

I tried to conclude and wrote a simple safe cast using templates. Note that this solution doesn't guarantee to cast pointers on a functions.

6
  • 2
    What? Why bother? This is precisely what reinterpret_cast already does in this situation: "An object pointer can be explicitly converted to an object pointer of a different type.[72] When a prvalue v of object pointer type is converted to the object pointer type “pointer to cv T”, the result is static_cast<cv T*>(static_cast<cv void*>(v))." -- N3797. Jan 7, 2016 at 19:22
  • As for c++2003 standard I can NOT find that reinterpret_cast does static_cast<cv T*>(static_cast<cv void*>(v)) Jan 12, 2016 at 14:03
  • 1
    OK, true, but I don't care about a version from 13 years ago, and nor should most coders if (as is likely) they can avoid it. Answers and comments should really reflect the latest available Standard unless otherwise specified... IMHO. Anyway, I guess the Committee felt the need to explicitly add this after 2003. (because IIRC, it was the same in C++11) Jan 15, 2016 at 22:48
  • 1
    Before C++03 it was C++98. Tons of projects used old C++ instead of portable C. Sometimes you have to care about portability. For example you have to support the same code on Solaris, AIX, HPUX, Windows. Where it comes to compiler dependency and portability it is tricky. So a good example of introducing a portability hell is to use a reinterpret_cast in your code Jan 16, 2016 at 21:24
  • again, if like me you're happy to limit yourself only to platforms that play nice with the latest and greatest version of the language, your objection is a moot point. Jan 17, 2016 at 0:10
-9

Quick answer: use static_cast if it compiles, otherwise resort to reinterpret_cast.

-21

Read the FAQ! Holding C++ data in C can be risky.

In C++, a pointer to an object can be converted to void * without any casts. But it's not true the other way round. You'd need a static_cast to get the original pointer back.

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.