Struct alignment and type reinterpretation

2019-06-27 23:19发布

问题:

Lets say I have two types A and B.

Then I make this type

struct Pair{
    A a;
    B b;
};

Now I have a function such as this.

void function(Pair& pair);

And lets assume that function will only ever use the a part of the pair.

Then is it undefined behavior to use and call the function in this way?

A a;
function(reinterpret_cast<Pair&>(a));

I know that a compiler may insert padding bytes after a member but can it also do it before the first member?

回答1:

I think it's defined behavior, assuming Pair is standard-layout. Otherwise, it's undefined behavior.

First, a standard layout class and its first member share an address. The new wording in [basic.compound] (which clarifies earlier rules) reads:

Two objects a and b are pointer-interconvertible if:
* [...]
* one is a standard-layout class object and the other is the first non-static data member of that object, or, [...]
* [...]
If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast (5.2.10).

Also from [class.mem]:

If a standard-layout class object has any non-static data members, its address is the same as the address of its first non-static data member. Otherwise, its address is the same as the address of its first base class subobject (if any).

So the reinterpret_cast from A to Pair is fine. If then function only ever access the a object, then that access well-defined, as the offset of A is 0, so the behavior is equivalent to having function take an A& directly. Any access to the b would be undefined, obviously.


However, while I believe the code is defined behavior, it's a bad idea. It's defined behavior NOW, but somebody someday might change function to refer to pair.b and then you're in a world of pain. It'd be a lot easier to simply write:

void function(A& a) { ... }
void function(Pair& p) { function(p.a); }

and just call function directly with your a.



回答2:

Yes, it's undefined behaviour.

In a struct pair, there can be padding between a and b. An assignment to a member of a struct can modify any padding in the struct. So an assignment to pair.a can modify memory where it thinks there is padding in the struct, where in reality there is just random memory following the memory occupied by your a.