Designated initializers

Last year, I discovered, thanks to the book “C, A Reference Manual“, a great C99 feature: designated initializers. Designated initializers allow you to initialize components of an aggregate (structure, union or array) by specifying their names within an initializer list.

Arrays initialization

What most people normally use to initialize an array is the following idiom:

int v[4] = { 4, 2, 1, -5 };

in which you need to initialize each component of the array sequentially. Designated initializers allow you to specify which component of the array you want to initialize. Thus, we could write the line above as:

int v[4] = { [1] = 2, [2] = 1, [0] = 4, [3] = -5 };

Note that we have specified the component indexes which has allowed us to initialize the array with our desired order. If we do not initialize all the components, those not initialized will get 0 values. We can also mix both methods, so the line below would be also correct:

int v[4] = { [1] = 2, 1, [3] = -5 };

in which the component not referenced goes right after the named one.

A possible use of this kind of initializations would be a mapping between a list of identifiers and a list of strings.

// The public interface

typedef enum {
} id_t;

extern char const* string_by_id (id_t id);

// The private implementation

static char const* strings[] =
  [id_one] = "identifier one",
  [id_two] = "identifier two",
  [id_three] = "identifier three"

char const*
string_by_id (id_t id)
  return strings[id];

Structures and unions initialization

Designated initializers are also useful to initialize components of structures and unions by their name. In this case, the component to be initialized takes the form .c, where c is the name of the component. So, suppose we have the following structure:

struct point { float x; float y; float z; };

we could initialize each component of a struct point variable like this:

struct point my_point =
  .x = 0.34,
  .y = 0.98,
  .z = 1.56

With unions, we will use the same method, so having the following union:

union integer
  unsigned char int_8;
  unsigned short int int_16; 
  unsigned long int_32;

we can initialize it by any of its components:

union integer value = { .int_16 = 24000 };

Finally, we can merge both cases, so we can have arrays of structures or unions that can be initialized using designated initializers:

struct point pointvector[3] =
  [0].x = 0.34, [0].y = 1.78, [0].z = 3.18,
  [1] = { .x = 3.5, .y = 6.89 },
  [2] = { .y = 2.8, 1.23 }

4 Responses to “Designated initializers”

  1. David Says:

    My favorite consequence of named initializers is this technique:

    struct a {
    int x;
    float y;
    char *z;

    void f1(struct a *);
    void f2()
    f1(&(struct a){.z=”Hello”, .y=.22, .x=10});

    Essentially you have a quick compact way of passing a struct into a function without needing a temporary variable. Obviously you can’t do this if the function is going to hold on to the pointer or pass you something back in the structure (that you care about). But it comes in really handy.


  2. Nit Picker Says:

    I agree about the usefulness of being be able to pass an ‘immediate’ struct as a parameter, but how is it a consequence of the named initializers feature ?

  3. dweej1 Says:

    David – how is your example any different from the following?:
    f1(&(struct a){10, 22, “Hello”});

    If there is no difference, using named initializers doesn’t provide anything special in your example.
    Also, in your example, would it be possible to assign a non-static to any of the struct members?:
    float foo = .1234;

    f1(&(struct a){.z=”Hello”, .y=foo, .x=10});

    If not, that further limits the usefulness of names initializers…

  4. aleix Says:

    I think that the main reason for designated initializers is to help reading code. It doesn’t provide any super extra cool feature, but it helps the reader to see the structure fields in an easy way (in the case of structures) or for example in the case of the string mapping I used in the post it also helps to read the code.

    In David’s example it might help reading the code if the reader does not remember in which order the two integers go.

    And yes, this is possible:

    float foo = .1234;

    f1(&(struct a){.z=”Hello”, .y=foo, .x=10});

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: