53

here is very simplified code of problem I have:

enum node_type {
    t_int, t_double
};

struct int_node {
    int value;
};

struct double_node {
    double value;
};

struct node {
    enum node_type type;
    union {
        struct int_node int_n;
        struct double_node double_n;
    };
};

int main(void) {
    struct int_node i;
    i.value = 10;
    struct node n;
    n.type = t_int;
    n.int_n = i;
    return 0;
}

And what I don't undestand is this:

$ cc us.c 
$ cc -std=c99 us.c 
us.c:18:4: warning: declaration does not declare anything
us.c: In function ‘main’:
us.c:26:4: error: ‘struct node’ has no member named ‘int_n’

Using GCC without -std option compiles code above without any problems (and the similar code is working pretty well), but it seems that c99 does not permit this technique. Why is it so and is it possible to make is c99 (or c89, c90) compatible? Thanks.

timrau
  • 22,578
  • 4
  • 51
  • 64
Martin
  • 1,473
  • 2
  • 12
  • 20
  • 3
    Just a note, clang compiles given code with and without `-std=c99` silently, without any errors and warnings. – Martin Jul 12 '10 at 11:58

7 Answers7

65

Anonymous unions are a GNU extension, not part of any standard version of the C language. You can use -std=gnu99 or something like that for c99+GNU extensions, but it's best to write proper C and not rely on extensions which provide nothing but syntactic sugar...

Edit: Anonymous unions were added in C11, so they are now a standard part of the language. Presumably GCC's -std=c11 lets you use them.

R.. GitHub STOP HELPING ICE
  • 208,859
  • 35
  • 376
  • 711
  • 1
    Update 2019, no need to use `-std=c11` with current stable releases for anonymous `struct` or `union`. Not sure what version before GCC 8.2 started adopting as standard. – Undefined Behavior Feb 26 '19 at 13:21
  • 1
    @UndefinedBehavior GCC 5 already expands `__STDC_VERSION__` as `201112L`, while GCC 4.8 doesn't expand it at all — both with default settings. So, apparently, it's GCC 5 that defaults to C11 (actually gnu11). – Ruslan Aug 26 '20 at 14:06
30

I'm finding this question about a year and a half after everybody else did, so I can give a different answer: anonymous structs are not in the C99 standard, but they are in the C11 standard. GCC and clang already support this (the C11 standard seems to have lifted the feature from Microsoft, and GCC has provided support for some MSFT extensions for some time).

bk.
  • 6,068
  • 2
  • 24
  • 28
  • 18
    The irony is that the semantics afforded by anonymous structs and unions were available in Dennis Ritchie's 1974 C compiler, and I think gcc had supported anonymous structs and unions in the days prior to the C89 Standard. Some people seem to think it's a new feature, but it merely represents a regained ability that should never have been lost. – supercat May 26 '18 at 17:55
5

Well, the solution was to name instance of the union (which can remain anonymous as datatype) and then use that name as a proxy.

$ diff -u old_us.c us.c 
--- old_us.c    2010-07-12 13:49:25.000000000 +0200
+++ us.c        2010-07-12 13:49:02.000000000 +0200
@@ -15,7 +15,7 @@
   union {
     struct int_node int_n;
     struct double_node double_n;
-  };
+  } data;
 };

 int main(void) {
@@ -23,6 +23,6 @@
   i.value = 10;
   struct node n;
   n.type = t_int;
-  n.int_n = i;
+  n.data.int_n = i;
   return 0;
 }

Now it compiles as c99 without any problems.

$ cc -std=c99 us.c 
$ 

Note: I am not happy about this solution anyway.

Martin
  • 1,473
  • 2
  • 12
  • 20
  • 3
    You should be happy! It's the Standard way to access union members, guaranteed to work with any C compiler since Jan 1st 1970. – Jens Aug 23 '12 at 07:11
  • 2
    It ugglies up the code somewhat, no idea why it wasn't included in K&R C, seems like a simple and useful feature to me... Anyway I use the same proxy method but define macros to avoid all the typing. – Arran Cudbard-Bell Dec 03 '14 at 03:16
  • 4
    I realize this is a very old post, but copying the actual code instead of a diff patch is much more readable. – ysap Apr 25 '16 at 01:10
  • 1
    @ysap but then how’d you spot the difference? – binki Aug 20 '16 at 01:21
  • 2
    @binki perhaps we need a DiffOverflow where people post snippets of code and answers consist of diffs that improve them (possibly based on one another) :) – Thomas Aug 22 '16 at 10:40
  • 1
    @Thomas it's called Github ;) – Gauthier Nov 18 '18 at 07:42
5

Just for clarifications about anonymous struct or anonymous union.

C11

6.7.2.1 Structure and union specifiers

An unnamed member whose type specifier is a structure specifier with no tag is called an anonymous structure; an unnamed member whose type specifier is a union specifier with no tag is called an anonymous union. The members of an anonymous structure or union are considered to be members of the containing structure or union. This applies recursively if the containing structure or union is also anonymous.

C99 There are no anonymous struct or union

Simplified: Type-specifier Identifier { Declaration-list } Tags ;

  • Type-specifier: struct or union;
  • Identifier: optional, your custom name for the struct or union;
  • Declaration-list: members, your variables, anonymous struct and anonymous union
  • Tags: optional. If you have a typedef in front of the Type-specifier, the Tags are alias and not Tags.

It is a anonymous struct or anonymous union only if it have no identifier and no tag, and exist inside another struct or union.

struct s {
    struct { int x; };     // Anonymous struct, no identifier and no tag
    struct a { int x; };   // NOT Anonymous struct, has an identifier 'a'
    struct { int x; } b;   // NOT Anonymous struct, has a tag 'b'
    struct c { int x; } C; // NOT Anonymous struct
};

struct s {
    union { int x; };     // Anonymous union, no identifier and no tag
    union a { int x; };   // NOT Anonymous union, has an identifier 'a'
    union { int x; } b;   // NOT Anonymous union, has a tag 'b'
    union c { int x; } C; // NOT Anonymous union
};

typedef hell: if you have a typedef the tag part is not a tag anymore, it is alias for that type.

struct a { int x; } A; // 'A' is a tag
union a { int x; } A;  // 'A' is a tag

// But if you use this way
typedef struct b { int x; } B; // 'B' is NOT a tag. It is an alias to struct 'b'
typedef union b { int x; } B;  // 'B' is NOT a tag. It is an alias to union 'b'

// Usage
A.x = 10; // A tag you can use without having to declare a new variable

B.x = 10; // Does not work

B bb; // Because 'B' is an alias, you have to declare a new variable
bb.x = 10;

The example bellow just change struct for union, work the same way.

struct a { int x; }; // Regular complete struct type
typedef struct a aa; // Alias 'aa' for the struct 'a'

struct { int x; } b; // Tag 'b'
typedef struct b bb; // Compile, but unusable.

struct c { int x; } C; // identifier or struct name 'c' and tag 'C'
typedef struct { int x; } d; // Alias 'd'
typedef struct e { int x; } ee; // struct 'e' and alias 'ee'

Undefined Behavior
  • 2,128
  • 4
  • 24
  • 34
  • 3
    You have tags backwards. The identifier immediately after `struct` or `union` is the tag. For `struct a { int x; } A;`, it's `a` that's the tag; `A` is a _variable_ of that `struct`. Once you have that correct, then there's not a special case for `typedef` since the `a` is _still_ the tag; it's just that the `A` becomes a _type_ instead of a variable. – Paul J. Lucas Oct 24 '21 at 14:14
1

Another solution is to put the common header value (enum node_type type) into every structure, and make your top-level structure a union. It's not exactly "Don't Repeat Yourself", but it does avoid both anonymous unions and uncomfortable looking proxy values.

enum node_type {
    t_int, t_double
};
struct int_node {
    enum node_type type;
    int value;
};
struct double_node {
    enum node_type type;
    double value;
};
union node {
    enum node_type type;
    struct int_node int_n;
    struct double_node double_n;
};

int main(void) {
    union node n;
    n.type = t_int; // or n.int_n.type = t_int;
    n.int_n.value = 10;
    return 0;
}
theJPster
  • 523
  • 5
  • 8
  • For the late readers like me: DRY could be avoided by use of a template and appropriate typedefs: `template struct base_node{/*[...]*/ T value;}; typedef base_node int_node;` – Aconcagua Sep 15 '15 at 14:43
  • 3
    In C++ maybe, but not in C99. – theJPster Oct 02 '15 at 16:13
  • Ah, sorry, somehow forgot about being in C while reading and thinking about... I've been too deep in C++ ultimately. – Aconcagua Oct 03 '15 at 08:29
  • I think that in the general case, there is no guarantee that the three members of the top union will be aligned, so your solution may not work. That said, I am not aware of any compiler that will not set the members in the intended alignment. – ysap Apr 25 '16 at 01:19
  • @theJPster: That's the traditional approach, and I think the Common Initial Sequence guarantees were intended to make it possible, but gcc and clang are either too primitive or too obtuse (take your pick) to support it. – supercat Jan 15 '19 at 22:09
1

Union must have a name and be declared like this:

union UPair {
    struct int_node int_n;
    struct double_node double_n;
};

UPair X;
X.int_n.value = 12;
Michael Foukarakis
  • 39,737
  • 6
  • 87
  • 123
Svisstack
  • 16,203
  • 6
  • 66
  • 100
  • 3
    Not in C11, but in C99 yes. But since we've passed the three year mark since its release, maybe it's time to start passing -std=c11 :). – Arran Cudbard-Bell Dec 03 '14 at 03:14
  • 2
    Your code example is in C++, not C. `union UPair` does not declare a `UPair` type in C. The namespaces for tags and types are separate in C but not in C++. – Patrick Schlüter Sep 14 '18 at 10:54
1

Looking at 6.2.7.1 of C99, I'm seeing that the identifier is optional:

struct-or-union-specifier:
    struct-or-union identifier-opt { struct-declaration-list }
    struct-or-union identifier

struct-or-union:
    struct
    union

struct-declaration-list:
    struct-declaration
    struct-declaration-list struct-declaration

struct-declaration:
    specifier-qualifier-list struct-declarator-list ;

specifier-qualifier-list:
    type-specifier specifier-qualifier-list-opt
    type-qualifier specifier-qualifier-list-opt

I've been up and down searching, and cannot find any reference to anonymous unions being against the spec. The whole -opt suffix indicates that the thing, in this case identifier is optional according to 6.1.

Cellcon
  • 1,245
  • 2
  • 11
  • 27
jer
  • 20,094
  • 5
  • 45
  • 69
  • 4
    I think there's a misunderstanding here. The identifier for a struct or union **tag** is optional, but not the identifier that's being declared. You can't say `union { ... };` within some aggregate for the same reason you can't say `int;`. In the union case, compiler extensions allow it, because you can use identifiers in the `{...}` part when using an anonymous union. – Jens Aug 22 '12 at 08:39