views:

143

answers:

4
+1  Q: 

lookup table in c

Hi,

I'm creating a lookup table in C When I define this:

typedef struct {
 char* action;
 char* message;
} lookuptab;

lookuptab tab[] = {
  {"aa","bb"},
  {"cc","dd"}
};

it compiles without errors but when I do something like this:

typedef struct {
 char* action;
 char* message[];
} lookuptab;

lookuptab tab[] = {
  {"aaa", {"bbbb", "ccc"}},
  {"cc", {"dd", "eeeee"}}
};

I get the following error:

error: initialization of flexible array member in a nested context

error: (near initialization for ‘tab[0].message’)

How can I initialize the tab array in the second example? Note: I know all the values inside the tab array.

UPDATE: message could be of different size, e.g

typedef struct {
 char* action;
 char* message[];
} lookuptab;

lookuptab tab[] = {
  {"aaa", {"bbbb", "ccc", "dd"}},
  {"cc", {"dd", "eeeee"}}
};

Thank you very much.

Best regards, Victor

+2  A: 

I think you have to specify the array size to use the struct in another array:

typedef struct {
 char* action;
 char* message[2];
} lookuptab;
bde
Either that, or just swallow the bitter pill and do the initialization using more than one line of code ;)
Stargazer712
Hi bde, thanks, your option works, but message could also be of different size, I just updated my question to reflect that. Sorry.
Victor Gaspar
BTW, using fix-max-size for message doesn't like me that much, neither.
Victor Gaspar
@Victor: In that case you would have to set the size of `message` to the largest possible size. Or just manage the memory by hand using pointers, as suggested in the other answer.
bde
+3  A: 

You can't use structures containing a flexible array member in an array (of the structure). See C99 standard §6.7.2.1/2:

A structure or union shall not contain a member with incomplete or function type (hence, a structure shall not contain an instance of itself, but may contain a pointer to an instance of itself), except that the last member of a structure with more than one named member may have incomplete array type; such a structure (and any union containing, possibly recursively, a member that is such a structure) shall not be a member of a structure or an element of an array.

So, use a char ** instead (and worry about how you know how many entries there are):

typedef struct
{
    const char         *action;
    const char * const *message;
} lookuptab;

static const lookuptab tab[] =
{
    { "aaa", (const char * const []){ "bbbb", "ccc"   } },
    { "cc",  (const char * const []){ "dd",   "eeeee" } }
};

This uses a C99 construct (§6.5.2.5 Compound literals) - beware if you are not using a C99 compiler.

Jonathan Leffler
Hi Jonathan! this code worked great! (and without warnings!)
Victor Gaspar
I'll just unpack my old C book to understand this construct ;) (Java is killing my engineering skills...)
Victor Gaspar
A: 
typedef struct {
 char* action;
 char* message[];
} lookuptab;

lookuptab is an incomplete type. You cannot create objects of that type. Either provide a definite size for the message array

typedef struct {
 char* action;
 char* message[42];
} lookuptab_definite_size;

or use pointers all around and manage memory "by hand"

typedef struct {
 char* action;
 char** message;
} lookuptab_pointers_all_around;

you can use the flexible array member (all elements will have the same size), but it's a lot of work :-)

#include <stdlib.h>
typedef struct {
  char* action;
  char* message[];
} lookuptab;

int main(void) {
  lookuptab *tab;

  tab = malloc(sizeof *tab + 42 * sizeof *tab->message);
  /* tab = malloc(elems * (sizeof *tab + 42 * sizeof *tab->message)); */
  /* tab[0] ... tab[elems-1] all have the same size */
  if (tab) {
    tab->action = NULL;
    tab->message[0] = NULL;
    tab->message[1] = NULL;
    /* ... */
    tab->message[41] = NULL;
    free(tab);
  }
  return 0;
}
pmg
You can't allocate an array of flexible-array-member structs like your comment suggests - the compiler has no way of knowing where `tab[1]` should start.
caf
In my code it says all array elements have the same size, I edit the comment to make it more visible. Thank you for pointing it out.
pmg
A: 

You need to specify a size for the message array member in the struct definition:

#define N ... // maximum number of elements in message array

typedef struct
{
  char *action;
  char *message[N]; 
} lookuptab;

lookuptab tab[] = {
  {"aa", {"bb", "cc"}},
  {"dd", {"ee", "ff"}},
  ...
};

In this case, N must be at least 2.

If you want each instance of the lookuptab struct to have a different number of elements in the message array, then you will have to allocate each message array separately, meaning you won't be able to use a static initializer:

typedef struct
{
  char *action;
  char **messages;
} lookuptab;

lookuptab *newEntry(const char *action, size_t numMessages, ...)
{
  lookuptab *entry = malloc(sizeof *entry);
  if (entry)
  {
    entry->action = malloc(strlen(action) + 1);
    if (entry->action)
      strcpy(entry->action, action);
    if (numMessages > 0)
    {
      entry->messages = malloc(sizeof *entry->messages * numMessages);
      if (entry->messages)
      {
        size_t i;
        va_list ap;

        va_start(ap, numMessages);

        for (i = 0; i < numMessages; i++)
        {
          char *nextMessage = va_arg(ap, char *);
          entry->messages[i] = malloc(strlen(nextMessage) + 1);
          if (entry->messages[i])
            strcpy(entry->messages[i], nextMessage);
        }
      }
    }
  }
  return entry;
}

int main(void)
{
  lookuptab *tab[ENTRIES]; // for some number of ENTRIES
  tab[0] = newEntry("AA", 2, "BB", "CC");
  tab[1] = newEntry("DD", 3, "EE", "FF", "GG");
  tab[2] = newEntry("HH", 0);
  ...
}

Instead of passing the number of messages explicitly, you can use a sentinel:

  tab[0] = newEntry("AA", "BB", "CC", NULL);

but you'll either have to cycle through all the arguments twice (first to get the number to allocate the messages array, then to copy each message) or you'll have to realloc() your array for each message, such as:

size_t numMessages = 0;
...
char *nextMessage
while ((nextMessage = va_arg(ap, char *)) != NULL)
{
  char **tmp = realloc(entry->messages, sizeof *entry->messages, numMessages+1);
  if (tmp)
  {
    entry->messages = tmp;
    entry->messages[numMessages] = malloc(strlen(nextMessage) + 1);
    strcpy(entry->messages[numMessages], nextMessage);
    numMessages++;
  }
}
John Bode