Skip to content

Anonymous and Nested Structs

MarekBykowski edited this page Jul 9, 2026 · 1 revision

Anonymous and Nested Structs in C

Notes on the difference between an unnamed struct type, a named (tagged) struct type, and a truly anonymous struct whose members are promoted into the enclosing aggregate.

TL;DR

Form Tag? Member name? Reusable type? Access
struct { ... } var; no — (var is the variable) no, dies at ; var.x
struct { ... } m; inside a struct no yes (m) no outer.m.x
struct { ... }; inside a struct/union no no no outer.x (promoted)
struct tag { ... }; yes yes struct tag v; v.x

1. Unnamed type, one variable

struct { int x; int y; } point1;

Declares an unnamed struct type and defines exactly one variable point1 of it, in the same statement.

  • No tag → the type can never be named again.
  • You cannot declare a second variable "of the same type" later.
  • Two separate anonymous struct definitions with identical members are distinct, incompatible types.

So this is genuinely one-shot.

2. Named (tagged) type, reusable

A tag is what buys reusability.

struct point { int x; int y; };     // defines the TYPE only, no variable
struct point point1, point2;        // two variables of that type

Or fuse the definition with the first declarations:

struct point { int x; int y; } point1, point2;   // type + two vars at once

Common malformed attempt:

struct point { int x; int y; };
struct point = point1, point2;      // ❌ `struct point =` is not a declaration

After the tag exists, declare variables as type followed by a variable list: struct point point1, point2;.

Namespace note

Tags live in a separate namespace from ordinary identifiers, so these are all legal:

struct point point1;   // tag `point`, variable `point1`  — fine
struct point point;    // tag `point`, variable `point`   — legal but don't

This is exactly why C programmers reach for a typedef:

typedef struct point { int x; int y; } point_t;   // now: point_t p;

3. Nested struct WITH a member name (all C versions)

Valid in every version of C. The inner struct has no tag, but the member is named, so you reach fields through the member.

struct outer {
    int id;
    struct {
        int x;
        int y;
    } point;          // no tag, but member is named "point"
};

struct outer o;
o.point.x = 1;        // access via the member name

4. Truly anonymous struct — members promoted (C11)

C11 §6.7.2.1p13 (a GCC/Clang extension long before C11). No tag and no member name → the inner members become members of the containing struct.

struct outer {
    int id;
    struct {
        int x;
        int y;
    };                // no tag, no member name -> anonymous
};

struct outer o;
o.x = 1;              // promoted into the enclosing struct
o.y = 2;

Tradeoffs:

  • x / y must not collide with any other member name in outer.
  • You can never name the inner type or take its sizeof — it has no name.

5. Where it earns its keep: register overlays

Anonymous struct inside an anonymous union — overlay a raw value with a bitfield view of the same storage, with no .bits. indirection.

struct ctrl_reg {
    union {
        uint32_t raw;
        struct {
            uint32_t enable : 1;
            uint32_t mode   : 2;
            uint32_t rsvd   : 29;
        };            // anonymous struct inside anonymous union
    };
};

struct ctrl_reg r;
r.raw    = 0;
r.enable = 1;         // same 32 bits as r.raw, flat access

Bitfield layout is implementation-defined, so this pattern is portable only within a known ABI/compiler — fine for kernel-on-a-known-target work.


6. Initialization gotcha

You can't use a designated initializer with the anonymous type's name (there isn't one), but you can designate the promoted member directly:

struct outer o = { .id = 5, .x = 1, .y = 2 };   // OK

7. Standard-conformance caveat

The C standard only blesses anonymous structs and unions. Anonymous members of other aggregate kinds, or nesting a named variable of an anonymous type, are not part of C11 — some of what GCC accepts there is -fms-extensions territory, not ISO C.


See also

  • __attribute__((packed))
  • Bitfields
  • Pointer arithmetic
  • container_of (interacts with anonymous inner structs via offsetof)

Clone this wiki locally