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.