Search code examples
cunions

Do pointers to different types match the "common initial sequence" rule?


If I have a union that contains pointers to different types of data, would it be legal to free malloced memory via a different field than the one it was assigned to? Does it even match the "common initial sequence" rule?

#include <stdlib.h>

typedef struct {
    int type;
    union {
        void *entries;
        long *long_entries;
        // etc
    } u;
} Bar;

int main (void) {
   Bar bar;
   bar.u.long_entries = malloc(6 * sizeof(long));
   free(bar.u.entries);
}

I'm inclined to say that this is legal, but I'm not entirely sure.


Considering the answers so far, I think I would have to change my code to something like this; which I expect is fully legal:

typedef struct {
    int type;
    void *entries;
} Bar;

int main(int argc, char *argv[]) {
    Bar bar;
    bar.entries = malloc(6 * sizeof(long));
    // ...
    long *long_entries = bar.entries;
    long_entries[3] = 123;
    // ...
    free(bar.entries);
}

Solution

  • If I have a union that contains pointers to different types of data, would it be legal to free malloced memory via a different field than the one it was assigned to? Does it even match the "common initial sequence" rule?

    • What you describe does not satisfy the criteria of the "common initial sequence" rule, as that is about union members that have structure types. Pointers are not structures.

    • Even if we were talking about structures containing pointers as members, pointers types are not compatible if their pointed-to types are not compatible. void and long are not compatible types, and probably Foo is not compatible with either, so the common initial sequence rule would not apply even if you wrapped the pointers in structures.

    • In C, you do not need to rely on the common initial sequence rule to read a different union member than was last written. In practice, it is very common for implementations to use the same representation for all object pointer types, and on such implementations, you probably could use any of those pointer members to free the pointed-to data. HOWEVER, C does not require all object pointer types to use the same representation, and there have historically been implementations in which they didn't. On one of those, the practice you describe most likely would not reliably behave as you intend. Such code can conform to the language spec, but it does not conform strictly. It is not portable.