Search code examples
arrayscstructcompound-literals

Use of compound literal inside a function to init a variable in C


This question follows this one : Is there a syntax in C to DIRECTLY INIT a struct member that is a const array of constant literal?.

Eric Postpischil gave me a solution using compound literals.

Now, I would like to initialise my variables in a function. But, inside a function, compound literal has automatic storage duration and datas are needed outside this function. To solve this problem, I used the static keyword but I am not sure that is a good idea.

This is the initial code (with initialisation in the main)

#include <stdio.h>

typedef struct {
  const char* name;
  const int nb;
  const char* const* list;
} valid_options_t;

void print_options (const valid_options_t* option) {
  printf("Option Name : %s\n",option->name);
  for (int i=0; i < option->nb; i++) printf("\t%s\n",option->list[i]);
  printf("---------\n");
}
int main() {

  const valid_options_t my_size_opts = {.name="size", .nb=3, .list= (const char * const[]) {"large", "medium", "small"} };
  const valid_options_t my_color_opts = {.name="color", .nb=4, .list= (const char * const[]) {"orange", "yellow", "white", "red"} };
  const valid_options_t my_form_opts = {.name="shape", .nb=2, .list= (const char * const[]) {"circle", "square"} };

  print_options (&my_size_opts);
  print_options (&my_color_opts);
  print_options (&my_form_opts);

}

And the code with initialisation in a function.

#include <stdio.h>

typedef struct {
  const char* name;
  const int nb;
  const char* const* list;
} valid_options_t;

void print_options (const valid_options_t* option) {
  printf("Option Name : %s\n",option->name);
  for (int i=0; i < option->nb; i++) printf("\t%s\n",option->list[i]);
  printf("---------\n");
}

void init_coumpond_literal(valid_options_t *(*my_opts)[3]) {
  (*my_opts)[0] = &(static valid_options_t){.name="size", .nb=3, .list= (static const char * const[]) {"large", "medium", "small"} };
  (*my_opts)[1] = &(static valid_options_t){.name="color", .nb=4, .list= (static const char * const[]) {"orange", "yellow", "white", "red"} };
  (*my_opts)[2] = &(static valid_options_t){.name="shape", .nb=2, .list= (static const char * const[]) {"circle", "square"} };
}

int main() {

  const int feature_nb=3;
  
  valid_options_t *my_size_opts[feature_nb];
  
  init_coumpond_literal(&my_size_opts);
  
  for (int i=0; i < feature_nb; i++)
    print_options (my_size_opts[i]);
}

Is it the right way to do that ? And how can I free these variables ?


Solution

  • The original code is fine for C23, which is yet to be published at the time of writing, but for portability to earlier C standards, the static qualifier needs to be removed from the compound literals, but then they would no longer have static storage duration as required, so another solution would be required. One possibility is to replace the compound literals with static variables of limited scope, for example:

    #define ARRAYLEN(a) (sizeof(a) / sizeof((a)[0]))
    
    void init_coumpond_literal(valid_options_t *(*my_opts)[3]) {
      static const char * const list_size[] = {"large", "medium", "small"};
      static const char * const list_color[] = {"orange", "yellow", "white", "red"};
      static const char * const list_shape[] = {"circle", "square"};
      static valid_options_t opts[3] = {
        {.name="size", .nb=ARRAYLEN(list_size), .list=list_size },
        {.name="color", .nb=ARRAYLEN(list_color), .list=list_color },
        {.name="shape", .nb=ARRAYLEN(list_shape), .list=list_shape }
      };
      (*my_opts)[0] = &opts[0];
      (*my_opts)[1] = &opts[1];
      (*my_opts)[2] = &opts[2];
    }
    

    EDIT: Removed non-constant static initializers for .list members in the above and replaced with more static variables. Thanks to Eric Postpischil for pointing out the error.

    The above initialization of opts could be simplified by defining a temporary macro:

    void init_coumpond_literal(valid_options_t *(*my_opts)[3]) {
      static const char * const list_size[] = {"large", "medium", "small"};
      static const char * const list_color[] = {"orange", "yellow", "white", "red"};
      static const char * const list_shape[] = {"circle", "square"};
      static valid_options_t opts[3] = {
    #define XOPT(x) { .name=#x, .nb=ARRAYLEN(list_##x), .list=list_##x } 
        XOPT(size),
        XOPT(color),
        XOPT(shape)
    #undef XOPT
      };
      (*my_opts)[0] = &opts[0];
      (*my_opts)[1] = &opts[1];
      (*my_opts)[2] = &opts[2];
    }
    

    The opts array is modifiable outside the function because it is not const. If that is undesirable, declare opts as static const valid_options_t opts[3] = …;, and make appropriate changes elsewhere in the code.