Jon Purdy Jon Purdy - 1 month ago 7
C Question

Casting structure pointer to union pointer

I have a

union
containing various compatible
struct
members:

enum Type { T1, T2, … };

struct S1 { enum Type type; … };
struct S2 { enum Type type; … };


union U {
enum Type type;
struct S1 as_s1;
struct S2 as_s2;

};


The
type
field of these structures is treated as immutable, and only the field of the union corresponding to the current
type
is accessed. I am aware that casting
union U *
to
struct S1 *
is defined as long as the
type
invariant is upheld, but is the reverse also true?

struct S1 *s1 = malloc (sizeof (struct S1));
union U *u = (union U *)s1;


That is, is it safe to allocate only
sizeof (struct S1)
bytes and cast the result to
union U *
? I would like to avoid allocating
sizeof (union U)
bytes, which in my case may be significantly larger.

Answer

The casts themselves are not likely to be a problem. They are permitted as long as the struct and union types have the same alignment requirement. Moreover, in that case the standard demands that if the value is converted back to the original type then the result shall compare equal to the original pointer, so no data is lost.

The problem is what happens when you access the pointed-to object as if it were a union U. It is likely that some things you could do that way would work out fine, but others are definitely unsafe. Specifically, you ask

That is, is it safe to allocate only sizeof (struct S1) bytes and cast the result to union U *?

No, it is not safe. The standard specifies:

When a value is stored in an object of structure or union type, including in a member object, the bytes of the object representation that correspond to any padding bytes take unspecified values. [...]

When a value is stored in a member of an object of union type, the bytes of the object representation that do not correspond to that member but do correspond to other members take unspecified values.

(C2011, 6.2.6/6-7)

If you trigger one of those provisions, especially the latter, and the allocated space is not as big as the union's representation, then the program exhibits undefined behavior. It is possible that you could avoid program actions that do so, but creating such extra constraints on program behavior cannot be characterized as "safe" in my book.

I would like to avoid allocating sizeof (union U) bytes, which in my case may be significantly larger.

Since you apparently intend to use the type member of the union to identify which member to use, how about doing this:

struct generic_s {
    enum Type type;
    void *contents;
};

struct generic_s s = { T1 };
s.contents = malloc(sizeof (struct S1));

Pass around objects of type struct_generic directly to avoid extra allocations and deallocations:

do_something(s);  // i.e. not do_something(&s)

You would need to cast the contents pointer to dereference it, of course. On the other hand, that's all just a bit of fancy wrapping around the pointer to identify the pointed-to type. You could instead do without the wrapping and pass around the type information explicitly.