简体   繁体   中英

passing reinterpret_cast pointer as void *

In the following code -

#include <iostream>
using namespace std;

struct S1{
    int a; int b; int c; int d;
};

struct S{
    int a; int b;
};

int main() {
    S1 obj1;
    obj1.a = 1;
    obj1.b = 2;
    obj1.c = 3;
    obj1.d = 4;

    cout << obj1.a << " "
         << obj1.b << " "
         << obj1.c << " "
         << obj1.d << "\n";

    // more code

    auto ptr = reinterpret_cast<S *>(&obj1);
    cout << ptr->a << " " << ptr->b << "\n";

    // more code

    // someFunc(ptr, sizeof(obj1));
}

I'm assigning values to members of structure S1 and then using them somewhere else as struct S pointer by reinterpret_cast ing the original S1 to S . This works fine.

Now a little ahead, I've a function someFunc(const void *ptr, int len) that takes a const void * pointer and length to the structure being passed. In this case, it is expected to pass S1 structure to it, and inside it it has some memcpy calls to copy the structure and work on that copy. So can I do this? -

// more code

   someFunc(ptr, sizeof(obj1));
}

meaning pass that reinterpret_cast ed pointer to this function and size of original S1 structure or do I have to again reinterpret_cast it back S1 and then pass the pointer again.

Not having to reinterpret_cast again to original type will save me some switch cases for similar types like S1 . Note that someFunc can easily find out type based on size passed in because those similar types differ greatly in size.

The address of a struct is the address of the first byte of the struct. Even if I could not find any reference in standard for it, it is a common idiom in C++ (as it was in C) to cast a pointer to a POD struct (more or less a C struct) to a pointer to an initial subsequence of that class. When converted to a void * , both will give same value.

So with your notations:

someFunc(ptr, sizeof(obj1));

and

someFunc(obj1, sizeof(obj1));

are exactly the same call.

Yes,

     someFunc(ptr, sizeof(obj1));

is fine ... but only because obj1 is actually of type S1 . I am a little suspicious of your comment "save me some switch cases for similar types like S1".

I'm assigning values to members of structure S1 and then using them somewhere else as struct S pointer by reinterpret_casting the original S1 to S. This works fine.

Now a little ahead, I've a function someFunc(const void *ptr, int len) that takes a const void * pointer and length to the structure being passed.

So can I do this?

You can. You just never should.

The only reason you should have such an API in your code, is if you didn't write it but MUST use it, have no right to touch the code it resides in, and/or have no time budget to wrap or rewrite it (this should take an hour or so).

Alternative code, using inheritance (avoids reinterpret_cast and the entire problem):

struct S { int a, b; }; // defines common behavior between structures
                        // this is what base classes are for

struct S1: S { int c, d; }; // inherit S

someFunc:

auto someFunc(S& sref);

S s{1, 2};
S1 s1{1, 2, 3, 4};
someFunc(s); // works
someFuncf(s1); // also works

Not having to reinterpret_cast again to original type will save me some switch cases for similar types like S1.

Having to type reinterpret_cast on user types (your S structures) is a sign that you haven't extracted common behavior into common base classes/structures.

I'm assigning values to members of structure S1 and then using them somewhere else as struct S pointer by reinterpret_casting the original S1 to S. This works fine.

This is undefined behavior. One possible symptom is that it "works fine".

S1 and S are layout compatible, but they are not the same object. Either an S1 or an S lives at that location, not both.

Accessing an object that isn't actually there is undefined behavior.

What you want is known as pointer-inconvertibility.

This is legal C and C++:

struct S{
  int a; int b;
};
struct S1{
  S s;
  int c; int d;
};

Now, a pointer to S1 can be reinterpreted to be a pointer to S , and the a and b are the same as the sa and sb in the original S1 .

In C++ this can also be done with inheritance:

struct S1:S {
  int c; int d;
};

In both these cases, there is both an S1 and an S object there, so you never access an object that isn't there.


What you have done will often work; but under the C++ standard it is undefined behavior.

As a practical problem, optimizers can assume it never happens, that no access to an S could modify the state of an S1 and vice versa, which could cause exceedingly unexpected behavior in your program. When you upgrade your program to link time optimization, new and extremely dangerous behavior could spontaneously appear.

S1 obj1 { 1,2,3,4 };
static_cast<S*>(&obj1).a=99;
std::cout << obj1.a; // can legally print out 1, or anything else really
std::cout << obj1.a; // it could now print out 99.  Why?  Undefined behavior

Once you have actual pointer interconvertibility, the C++ standard does guarantee that:

S1 obj1 { 1,2,3,4 };
auto* s = reinterpret_cast<S*>(&obj1);
void* ptr = s;
Assert( static_cast<S1*>(ptr) == &obj1 );

the Assert passes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM