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 ?
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.