3

If the first k fields of record type T1 and T2 are identical, are these fields guaranteed to have the same offset? For instance, is this program well-defined?

typedef struct {
    int x, y;
} Shape;

typedef struct {
    int x, y;
    int w, h;
} Rectangle;

static void InitShape(Shape *s)
{
    s->x = 0;
    s->y = 0;
}

int main(void)
{
    Rectangle r;

    InitShape((Shape *) &r);
    r.w = 1;
    r.h = 1;
    return 0;
}

Edit: In my project the C code is generated and including the individual fields from Shape in Rectangle (instead of a single field base of type Shape) simplifies the code elsewhere.

August Karlstrom
  • 10,773
  • 7
  • 38
  • 60
  • Probably - but I would not bet on it. Why would one assume that they do? – Ed Heal Nov 13 '16 at 17:57
  • 1
    Have a look at ["Struct inheritance in C"](http://stackoverflow.com/questions/1114349/struct-inheritance-in-c). – Schwern Nov 13 '16 at 17:57
  • Doesn't casting one type to another violate the strict aliasing rule? Although downvoted, that answer has a good idea to include one struct in the other. – Weather Vane Nov 13 '16 at 17:58

3 Answers3

3

The program is not defined.

C standard only guarantees that there shall be no padding before the first member of the struct. This means that there may be different amount of padding between members x and y of structs Shape and Rectangle, and when one object of type Rectangle is reinterpreted as type Shape, it might not have the members at the same offsets.

This is not the main problem as it is reasonable to expect that both members will have the same offsets, and this can be enforced using static asserts.

The main problem is strict aliasing. There are conditions under which the types may alias1, but those two types don't satisfy any of them. The types Rectangle and Shape are not compatible, and the type Rectangle does not contain a member of type Shape.

Thus reinterpreting the type Rectangle as the type Shape: InitShape((Shape *) &r);, causes undefined behavior.


1 6.5 Expressions 7
An object shall have its stored value accessed only by an lvalue expression that has one of the following types:
— a type compatible with the effective type of the object,
— an aggregate or union type that includes one of the aforementioned types among its members (including, recursively, a member of a subaggregate or contained union)

Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
2501
  • 25,460
  • 4
  • 47
  • 87
  • I would love to read constructive comments along with downvotes on a correct answer. – 2501 Nov 15 '16 at 02:52
-1

It will work. The function will initialize the x and y fields of the structure as if it were a shape. I've seen many projects that use something similar to that, they create several structures with the same first field to differentiate between them (take a look at the love game, written by quel solaar) and it's actually quite useful if you use it wisely.

However I'd do it differently. When the initial data is the same, I'd actually modify the Rectangle structure like this:

typedef struct { 
    Shape s;
    int w, h;
} Rectangle;

This way, when you call the init function you can either typecast the rectangle as a shape or pass the internal Shape s to the function. In my mind it makes it clearer.

Going further I'd do something more similar to this:

#include <stdint.h>

#define TYPE_SHAPE 0U
#define TYPE_RECTANGLE 1U

typedef struct {
    char *name;
    char *descrition;
    uit8_t type;
} Info;

typedef struct {
    Info info;
    int x, y;
} Shape;

typedef struct {
    Info info;
    Shape s;
    int w,h;
}

void Init(Info *block) {
    if(block->type == TYPE_SHAPE) {
        //initialize it
    } else {
        //do something elese
    }
}

Even better I'd use an enum instead of a define for the type, and the field would change to an enum, but for this size I think it's ok that way.

But again, answering your question again, IF the x and y fields of both the rectangle and shape mean the same thing, then your way will work without any problems.

PS: I haven't compiled the above to test if it works, it's just to give an idea.

morcillo
  • 1,091
  • 5
  • 19
  • 51
  • In my application of this technique the C code is generated; in this case it makes more sense to generate the individual fields of the base type in the extended type. – August Karlstrom Nov 13 '16 at 18:01
-1

The C99 Standard added (and C11 retains) a requirement that code which uses the Common Initial Sequence guarantee must ensure that a complete union type featuring the structures involved must be visible when they are accessed, so as to let the compiler know they might alias.

The Standard does not require that accesses be performed using the union type, but the author of gcc either believe or pretend that it does, notwithstanding the fact that there would be no reason to specify that the complete union type be visible under such an interpretation, and notwithstanding the fact that such an interpretation renders the Common Initial Sequence guarantee virtually worthless.

I would recommend adding a union type declaration to ensure compatibility with compilers that actually follow the Standard, but I would also recommend that if you're using gcc you use -fno-strict-alias regardless, since the authors of gcc have overtly refused to process strictly-conforming code in a fashion consistent with the Standard.

supercat
  • 77,689
  • 9
  • 166
  • 211