2

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_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. 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_casted 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.

8
  • @LogicStuff yes and I've marked it bold this time. Commented Feb 21, 2017 at 12:05
  • @hg_git Why are you using reinterpret_cast here? Commented Feb 21, 2017 at 12:21
  • @TheApache because S1 and S are two different types. Commented Feb 21, 2017 at 12:50
  • Sounds like an XY problem. What are you actually trying to achieve here? Commented Feb 21, 2017 at 12:53
  • @NathanOliver I have similar standard layout types S1, S2, S3, S4 with Common Initial Sequence matching with type S. For some library, they all need to be casted to type S pointer. Then I need to pass them in another function that takes const void * and length of original type. So is it safe to pass the casted to S pointer and length of original type S1 or S2.. whichever it may be. Here - ideone.com/TIzNS8 Commented Feb 21, 2017 at 13:01

4 Answers 4

2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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".

3 Comments

because there are some other types like S1 - S2, S3, S4 with more members than S1 in them but Common Initial Sequence matching with S as well. So when I create a pointer to one of them this way I won't have to cast it back and pass them to someFunc. But if I need to cast back, I will have to put a switch case on their length and cast back to new pointers of original type and then pass them.
Here - ideone.com/TIzNS8 . The similar types S1, S2.. won't interact with one another, only casted to S, used properly and then passed to function with their original length. Is this safe?
That's fine. You are using the correct length for each ptr.
1

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.

2 Comments

I would've done inheritance if I could, but that is a c library so no such features are allowed at places we use it.
Then consider wrapping it: Add a layer of C++ classes over the C structures that hides the casting and only expose those in your code.
1

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 s.a and s.b 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.

2 Comments

The C library I am using specifically mentions that there is no strict alias violation and they've taken care of it. I cannot redefine the structures as they are provided by them only. I can only construct and pass them..
@hg_git The C++ code outside of the library you have above has strict aliasing violations. The C library you are using doesn't matter, it cannot magically make undefined behavior in your code go away. In C++, you cannot reinterpret an actual S1 to be an S and it not be undefined behavior. As it happens, naive unoptimized code will often work; that isn't evidence there is no problem, but just hides the problem. This is independant of anything the library itself does. If you only ever construct S1 and S and never reinterpret cast them, you could be safe.

Your Answer

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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.