When to use reinterpret_cast?

前端 未结 11 1206
我寻月下人不归
我寻月下人不归 2020-11-22 08:41

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

相关标签:
11条回答
  • 2020-11-22 09:29

    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.

    0 讨论(0)
  • 2020-11-22 09:30

    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 讨论(0)
  • 2020-11-22 09:37

    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__; })
    
    0 讨论(0)
  • 2020-11-22 09:38

    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; }
    
    0 讨论(0)
  • 2020-11-22 09:44

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

    0 讨论(0)
提交回复
热议问题