When are anonymous structs and unions useful in C11?
Asked Answered
H

6

70

C11 adds, among other things, 'Anonymous Structs and Unions'.

I poked around but could not find a clear explanation of when anonymous structs and unions would be useful. I ask because I don't completely understand what they are. I get that they are structs or unions without the name afterwards, but I have always (had to?) treat that as an error so I can only conceive a use for named structs.

Himyarite answered 19/1, 2012 at 20:20 Comment(1)
@wallyk not really the same question.Aquarelle
J
77

Anonymous union inside structures are very useful in practice. Consider that you want to implement a discriminated sum type (or tagged union), an aggregate with a boolean and either a float or a char* (i.e. a string), depending upon the boolean flag. With C11 you should be able to code

typedef struct {
    bool is_float;
    union {
       float f;
       char* s;
    };
} mychoice_t;

double as_float(mychoice_t* ch) 
{ 
   if (ch->is_float) return ch->f;
   else return atof(ch->s);
}

With C99, you'll have to name the union, and code ch->u.f and ch->u.s which is less readable and more verbose.

Another way to implement some tagged union type is to use casts. The Ocaml runtime gives a lot of examples.

The SBCL implementation of Common Lisp does use some union to implement tagged union types. And GNU make also uses them.

Junkie answered 19/1, 2012 at 20:26 Comment(3)
ah, so wait, is the issue at hand that C11 adds support for'Anonymous structs and unions INSIDE of structs/unions"?Himyarite
At least that the most useful situation I can quickly imagine. In fact, GCC supported that as an extension long time ago, and I always blessed it for that...Junkie
@Renato in c99 <stdbool.h> provides bool.Ailment
S
67

A typical and real world use of anonymous structs and unions are to provide an alternative view to data. For example when implementing a 3D point type:

typedef struct {
    union{
        struct{
            double x; 
            double y;
            double z;
        };
        double raw[3];
    };
}vec3d_t;

vec3d_t v;
v.x = 4.0;
v.raw[1] = 3.0; // Equivalent to v.y = 3.0
v.z = 2.0;

This is useful if you interface to code that expects a 3D vector as a pointer to three doubles. Instead of doing f(&v.x) which is ugly, you can do f(v.raw) which makes your intent clear.

Socio answered 14/10, 2014 at 12:53 Comment(5)
This seems like it's venturing heavily into UB territory. Is there something in the standard that mandates that this will work?Monetmoneta
@EmilyL.: C89 never tried to define all forms of aliasing that quality implementations should support (since that should depend upon a compiler's platform and intended use case) and consequently failed to ensure behavior is defined even in many cases where it obviously should be (since quality implementations should support such cases whether or not the Standard compels it). Since array accesses are done through pointer decomposition and arithmetic, deliberately-obtuse compilers could apply creative "optimizations" with the fact that a struct is being accessed using a pointer which...Flanna
...may be of a member type, but was not formed by taking the address of the member accessed thereby. Given that compilers are evolving in the direction of greater aggressiveness rather than greater sanity, and are willing to use extremely strained interpretations of the Standard to justify their behavior, I wouldn't trust compilers to usefully process code like the above without -fno-strict-aliasing.Flanna
@EmilyL.: I should have added: C89 didn't specify such things, and since the world survived for ten years subsequent standards haven't seen the need to so either, even though the reason the world didn't end was that compilers weren't as aggressive as the Standard allowed. While I haven't tested the exact code above in godbolt, an interpretation of the Standard that would justify gcc's behavior in some other situations would probably let a compiler wreak havoc with the above code as wellFlanna
Why do you need the outer struct? Why can't you typedef the union directly to vec3d_t?Jdavie
L
13
struct bla {
    struct { int a; int b; };
    int c;
};

the type struct bla has a member of a C11 anonymous structure type.

struct { int a; int b; } has no tag and the object has no name: it is an anonymous structure type.

You can access the members of the anonymous structure this way:

struct bla myobject;
myobject.a = 1;  // a is a member of the anonymous structure inside struct bla   
myobject.b = 2;  // same for b
myobject.c = 3;  // c is a member of the structure struct bla
Lafond answered 19/1, 2012 at 20:29 Comment(11)
And what is the difference to just do struct bla {int a;intb;intc;};?Deice
@Zaibis there is no difference to access the members of the structure but the version with the anonymous structure holds an extra information: there is some logical relationship between a and b that doesn't exist with cLafond
can you explain what this information could be usefull for? is this a thing of performance? or what is it about?Deice
@Zaibis take the example of an API, this information could be useful for the reader as it exposes a different information on the nature of a and b.Lafond
@Zaibis - The inner struct can be named and used on its own. One use case would be implementing inheritance (the outer struct extends the inner one).Rosamariarosamond
@martinkunev: are you sure about that it behaves like that?Deice
@Zaibis Yes, the standard (C99 6.7.2.1) guarantees that a pointer to a struct can be used as a pointer to its first member (with appropriate casting).Rosamariarosamond
@martinkunev: Of cause that is possible. but you said this is a way of implementing inheritance. that would mean the inner one would have its own a. how ever to acces it.Deice
@Zaibis I'm not sure I understand what you are asking. You can access the fields of the inner struct in exactly the same way as in the answer of ouah.Rosamariarosamond
yeah, but I that has nothing to do with inerhitance.Deice
The benefit to this would be with bitfields, to pack the struct with variable length integers taking up the same space as a single int.Oppenheimer
B
8

Another useful implementation is when you are dealing with rgba colors, since you might want access each color on its own or as a single int.

typedef struct {
    union{
        struct {uint8_t a, b, g, r;};
        uint32_t val;
    };
}Color;

Now you can access the individual rgba values or the entire value, with its highest byte being r. i.e:

int main(void)
{
    Color x;
    x.r = 0x11;
    x.g = 0xAA;
    x.b = 0xCC;
    x.a = 0xFF;

    printf("%X\n", x.val);

    return 0;
}

Prints 11AACCFF

Botch answered 2/2, 2020 at 7:22 Comment(1)
Perhaps you are just showing that you can do this but why would you use the outside struct? If you remove the outside struct and typedef the union, your code appears to behave the same.Mejias
Z
5

I'm not sure why C11 allows anonymous structures inside structures. But Linux uses it with a certain language extension:

/**
 * struct blk_mq_ctx - State for a software queue facing the submitting CPUs
 */
struct blk_mq_ctx {
    struct {
        spinlock_t      lock;
        struct list_head    rq_lists[HCTX_MAX_TYPES];
    } ____cacheline_aligned_in_smp;

    /* ... other fields without explicit alignment annotations ... */

} ____cacheline_aligned_in_smp;

I'm not sure if that example strictly necessary, except to make the intent clear.

EDIT: I found another similar pattern which is more clear-cut. The anonymous struct feature is used with this attribute:

#if defined(RANDSTRUCT_PLUGIN) && !defined(__CHECKER__)
#define __randomize_layout __attribute__((randomize_layout))
#define __no_randomize_layout __attribute__((no_randomize_layout))
/* This anon struct can add padding, so only enable it under randstruct. */
#define randomized_struct_fields_start  struct {
#define randomized_struct_fields_end    } __randomize_layout;
#endif

I.e. a language extension / compiler plugin to randomize field order (ASLR-style exploit "hardening"):

struct kiocb {
    struct file     *ki_filp;

    /* The 'ki_filp' pointer is shared in a union for aio */
    randomized_struct_fields_start

    loff_t          ki_pos;
    void (*ki_complete)(struct kiocb *iocb, long ret, long ret2);
    void            *private;
    int         ki_flags;
    u16         ki_hint;
    u16         ki_ioprio; /* See linux/ioprio.h */
    unsigned int        ki_cookie; /* for ->iopoll */

    randomized_struct_fields_end
};
Zootoxin answered 15/5, 2019 at 11:7 Comment(0)
B
0

Well, if you declare variables from that struct only once in your code, why does it need a name?

struct {
 int a;
 struct {
  int b;
  int c;
 } d;
} e,f;

And you can now write things like e.a,f.d.b,etc.

(I added the inner struct, because I think that this is one of the most usages of anonymous structs)

Bulldozer answered 19/1, 2012 at 20:24 Comment(3)
This was correct and submitted a little earlier than the one I accepted. Sorry, that one 'explained' it a little better but, now that I understand, I see that this is a pretty good answer.Himyarite
This is not the feature being discussed, and this code is not making use of anything new to C11. The structs in the example are not anonymous: they have the names .d, e and f respectively. They have anonymous types, but that's something different.Regulator
anonymous structs has no identifier and no tag.Novice

© 2022 - 2024 — McMap. All rights reserved.