Accessing first field of struct in a union of structs

2.1k Views Asked by At

I have three structs that share the first type and name of the first field:

struct TYPEA {
  char *name;
  int x,y;   /*or whatever*/
};

struct TYPEB {
  char *name;
  float a[30]; /*or whatever*/
};

struct TYPEC {
  char *name;
  void *w,*z; /*or whatever*/
};

If I remember correctly the first field of a struct is required to start at the same address as the struct itself.

This makes me wonder if the same holds for a union:

union data {
  struct TYPEA;
  struct TYPEB;
  struct TYPEC;
};

union data *p = function_returning_a_sane_default_for_union_data();
printf("%s", (char*) p);

I have 2 question regarding this:

  1. are unions required by standard to always have their content at the same address?
  2. would it work if the structs all had the same fiels, so differing only in name?
1

There are 1 best solutions below

1
On BEST ANSWER

The first element of a struct or union is guaranteed to have the same address-value as the struct´/union itself. Apparently it has not the same type!

For your usage, you do not need the cast and actually should avoid it:

6.5.2.3p6: One special guarantee is made in order to simplify the use of unions: if a union contains several structures that share a common initial sequence (see below), and if the union object currently contains one of these structures, it is permitted to inspect the common initial part of any of them anywhere that a declaration of the completed type of the union is visible. …

So you could (see below) simply

printf("%s", p->name);

(Note: that your usage of unnamed union members is not standard compilant. It is a (very useful) gcc extension (-fms-extensions, at least also supported by MSVC, too).)

But: The code in your question is wrong. You have to name each union member or have the type declarator with each member. With the same first member, It will not weork, though, because the names of the mebers of such unnamed members have to be unique (how else are they supposed to be accesseed individually?). So this will not really work. What you could do is:

union data {
    struct TYPEA typea;
    struct TYPEB typeb;
    struct TYPEC typec;
};

and

printf("%s", p->typea.name);

even if the struct contains a value of TYPEB currently.


An alternative and more clear way, would be to wrap the union into a struct:

struct TypeA {
    int x,y;
};

...

struct data {
    char *name;
    union {
        struct TypeA;
        struct TypeB;
        struct TypeC;
    };
};

This also uses the gcc extension at two levels: for the outer struct and the union. As such, it requires unique names for all possible paths. If you want to be 100% compliant, name each member like above and use the full path on access.

Note: I removed the name member from the inner structs in the union and moved it to the outer struct. I also changed names. The only well accepted naming convention in C is to use all-uppercase for macros only.