1

I have an enum like that:

enum {
  ID_FOO = 0,
  ID_BAR,
  ID_BAZ
}

And a constant array using designated initializers like hat:

char* arr[] = {
  [ID_FOO] = "foo stuff",
  [ID_BAR] = "bar stuff",
  [ID_BAZ] = "baz stuff",
  0
}

Now when I add a value to the enum, e.g. after ID_FOO but forget to add it to the array, then I would get an uninitialized null-initialized 'hole' in the array. Is there any way to prevent that, or at least get a warning from the compiler?

A non-portable GCC-only solution is fine.

Michael
  • 8,920
  • 3
  • 38
  • 56
  • 6
    If there is any initialiser at all, elements without explicit initialiser are initialised to (a type-appropriate value of) zero, so there's no uninitialised hole. But I guess you want to be warned about the default null pointers here? – Daniel Fischer Apr 23 '13 at 13:29
  • @DanielFischer yes, I don't want any null pointers in between. – Michael Apr 23 '13 at 13:51
  • @DanielFischer Do you have a reference for that? – abergmeier Dec 11 '19 at 20:46
  • 1
    @abergmeier In n1570 it was 6.7.9, clause 19: "all subobjects that are not initialized explicitly shall be initialized implicitly the same as objects that have static storage duration." – Daniel Fischer Dec 11 '19 at 20:53

2 Answers2

7

One way is to add a sentinel maximum value to your enum, which you can use to verify that this maximum value is the same as the number of elements in the array.

enum {
    ID_FOO = 0,
    ID_BAR,
    ID_BAZ,
    // insert new values here

    ID_MAX
}

assert(ID_MAX == (sizeof(arr)/sizeof(arr[0]) - 1));

This is a runtime check; have a look at C compiler asserts - how to implement? for ideas on how to get a compile-time error instead.

Community
  • 1
  • 1
Graham Borland
  • 60,055
  • 21
  • 138
  • 179
4

you can use X-Macros to keep them in sync, although some may argue about the prettiness of the resulting code.

The idea is to take all the information you need for both structures and put it into a single macro:

entries.inc

ENTRY(ID_FOO, "foo stuff")
ENTRY(ID_BAR, "bar stuff")
ENTRY(ID_BAZ, "baz stuff")

And then later, redefine your macro such that for each structure you need to build, you pull out the appropriate part from your data:

foo.c

/* here define what entry should be for your enums */
#define ENTRY(id, name) id,

enum {
#include "entries.inc"
};

/* and then redefine for the char array and include again */
#undef  ENTRY
#define ENTRY(id, name) [id] = name,

char* arr[] = {
  #include "entries.inc"
  0
};


int main(int argc, char* argv[]) {
  /* whatever */
}
John Ledbetter
  • 13,557
  • 1
  • 61
  • 80