Initialization of flexible array member in this way is not allowed as per C standard.
C11: 6.7.2.1 Structure and union specifiers (p20-21):
21 EXAMPLE 2 After the declaration:
struct s { int n; double d[]; };the structure struct
shas a flexible array memberd. [...]22 Following the above declaration:
struct s t1 = { 0 }; // valid struct s t2 = { 1, { 4.2 }}; // invalid t1.n = 4; // valid t1.d[0] = 4.2; // might be undefined behaviorThe initialization of
t2is invalid (and violates a constraint) becausestruct sis treated as if it did not contain memberd. [...]
But, GCC allows the static initialization of flexible array:
GCC Manual: 6.17 Arrays of Length Zero:
Answer from haccks on Stack OverflowInstead GCC allows static initialization of flexible array members. This is equivalent to defining a new structure containing the original structure followed by an array of sufficient size to contain the data. E.g. in the following,
f1is constructed as if it were declared likef2.struct f1 { int x; int y[]; } f1 = { 1, { 2, 3, 4 } }; struct f2 { struct f1 f1; int data[3]; } f2 = { { 1 }, { 2, 3, 4 } };
You cannot initialize a flexible array member. Per C 2018 6.7.2.1 18, in most situations, a flexible array member is ignored. This is one of those situations: In initialization, it is as if the member does not exist.
Creating a structure with a flexible array member in a declaration statement can only create a structure with zero members in the array. To create a structure with more members in the array, you should dynamically allocate space with malloc or a similar routine. Once you have allocated sufficient space, you may convert the pointer to a pointer to a Category and then assign (rather than initialize) values to its members, including elements of its flexible array member.
(Conceivably, there might be a horrible way to define a union containing a structure with a flexible array member in such a way that sufficient space is provided for elements of the array, by stuffing the union with a char array of the necessary size. You still could not initialize those elements, but you could assign to them. Do not do this.)
To set up some prototype Category objects, you could use:
static const Category category1 = {"Academic Registrar"};
static const Category category2 = {"Financial Services"};
static const Category category3 = {"IT Support", "IT"};
static const Category category4 = {"Parking Police", "PP"};
static const Category category5 = {"Coop Placement", "CP"};
Later, to create a Category with several Ticket elements, you could do:
Category *C = malloc(sizeof *C + nTickets * sizeof(Ticket));
// Test C for NULL in case malloc failed.
// Copy initial data into new Category.
*C = category3;
// Assign values to Ticket elements.
for (int i = 0; i < nTickets; ++i)
{
C->ticket[i] = rand();
C->timestamp = 0;
}
Note that you cannot create an array of Category objects, because they have flexible size, and objects in an array can only have a fixed size. So you can only work with one Category at a time. The C pointer above can only point to one Category, not an array of them. But the C->ticket member is an array of Ticket.
Note that I do not necessarily recommend copying data into a Category from prototypes that way. If the data is short, it could just be assigned in code. If the data is long, it might be better to use pointers to data instead of copying all the data into each one. Good solutions depend on circumstances. The above code is given just as an example.
For const structures of static duration, a workable approach in most cases is to declare a structure which has the same members as the FAM version, but with the flexible array member replaced by a suitably-sized array, and then construct a union which contains that type as well as the one with the Flexible Array Member. The Common Initial Sequence guarantees imply that either type may be used to inspect the common members, and if an implementation gives any heed whatsoever to footnote 88 of the C11 draft (N1570) which says the purpose of 6.5p7 is to specify the circumstance when things may alias, it will refrain from using the rule as an excuse to break such code, since references to storage that is never modified never alias (aliasing involves situations where re-ordering accesses to an object would affect the semantics thereof; if an object is never modified, the order of accesses will never have any observable effect).
In theory, an implementation would be allowed to place a flexible array member at a different offset from a fixed-sized array, but code could, if desired, use a static assertion to ensure that does not happen. A deliberately-obtuse interpretation might break things, but since the One Program Rule would allow deliberately-obtuse implementations to break just about any program, that's not really saying much.
Question about combination of flexible array member and compound literal with explicit size
Is there a standard way to statically initialize flexible array members in C? - Stack Overflow
c - Non-static initialization of a flexible array member? - Stack Overflow
c - Error: non-static initialization of a flexible array member in Some Cases but Not Others - Stack Overflow
No, flexible arrays must always be allocated manually. But you may use calloc to initialize the flexible part and a compound literal to initialize the fixed part. I'd wrap that in an allocation inline function like this:
Copytypedef struct person {
unsigned age;
char sex;
size_t size;
char name[];
} person;
inline
person* alloc_person(int a, char s, size_t n) {
person * ret = calloc(sizeof(person) + n, 1);
if (ret) memcpy(ret,
&(person const){ .age = a, .sex = s, .size = n},
sizeof(person));
return ret;
}
Observe that this leaves the check if the allocation succeeded to the caller.
If you don't need a size field as I included it here, a macro would even suffice. Only that it would be not possible to check the return of calloc before doing the memcpy. Under all systems that I programmed so far this will abort relatively nicely. Generally I think that return of malloc is of minor importance, but opinions vary largely on that subject.
This could perhaps (in that special case) give more opportunities to the optimizer to integrate the code in the surroundings:
Copy#define ALLOC_PERSON(A, S, N) \
((person*)memcpy(calloc(sizeof(person) + (N), 1), \
&(person const){ .age = (A), .sex = (S) }, \
sizeof(person)))
Edit: The case that this could be better than the function is when A and S are compile time constants. In that case the compound literal, since it is const qualified, could be allocated statically and its initialization could be done at compile time. In addition, if several allocations with the same values would appear in the code the compiler would be allowed to realize only one single copy of that compound literal.
There are some tricks you can use. It depends on your particular application.
If you want to initialise a single variable, you can define a structure of the correct size:
Copy struct {
int age;
char sex;
char name[sizeof("THE_NAME")];
} your_variable = { 55, 'M', "THE_NAME" };
The problem is that you have to use pointer casting to interpret the variable as "person"(e.g. "*(person *)(&your_variable)". But you can use a containing union to avoid this:
Copyunion {
struct { ..., char name[sizeof("THE_NAME")]; } x;
person p;
} your_var = { 55, 'M', "THE_NAME" };
so, your_var.p is of type "person". You may also use a macro to define your initializer, so you can write the string only once:
Copy#define INIVAR(x_, age_, sex_ ,s_) \
union {\
struct { ..., char name[sizeof(s_)]; } x;\
person p;\
} x_ = { (age_), (sex_), (s_) }
INIVAR(your_var, 55, 'M', "THE NAME");
Another problem is that this trick is not suitable to create an array of "person". The problem with arrays is that all elements must have the same size. In this case it's safer to use a const char * instead of a char[]. Or use the dynamic allocation ;)
Hello everyone!
There is compound literal in C so i can easily declare array like this:
int arr[] = {1, 2, 3};Or even specify it's length:
int arr[] = (int [10]) {1};There is also a flexible array member support in struct, so i can do runtime initialization like this:
typedef struct
{
int len;
uint8_t Data[];
} struct_t;
struct_t test_struct =
{
.len = 3,
.Data = {1, 2, 3}
};However i cannot combine flexible array member with explicit compound literal size:
struct_t test_struct =
{
.len = 10,
.Data = (uint8_t [10]) { 0 }
};This code gives me error "initialization makes integer from pointer without a cast" in GCC. How can i specify compound literal size for flexible array member? That would be very handy for certain runtime initialization.
You can't do it with a flexible array member.
Instead, you can use char **argv and initialize it using compound literals.
typedef struct {
char **argv;
} Entry;
const Entry table[] = {
{ (char *[]) { "a", NULL } },
{ (char *[]) { "a", "b", "c", NULL } }
};
I added NULL to each of the arrays so the application can tell their lengths (the real argv has this as well).
You do not store the count of elements of argv. How would you know that the first one has 1 element, and the second one has 3? You have to store that, for example, in another array member.
You can define a structure that has the proper exact same static memory layout as the array of structures with flexible array members that you want to have. Then you can then alias the structure with a pointer to Entry, and use that as an array of entries. Following example does that:
#include <stdlib.h>
#include <assert.h>
#include <stdalign.h>
#include <stddef.h>
#include <stdio.h>
typedef struct {
unsigned len;
char *argv[];
} Entry;
// sane iterating
static_assert(alignof(Entry) == alignof(char *), "");
typedef struct {
unsigned len;
char *argv[1];
} Entry_1;
static_assert(alignof(Entry_1) == alignof(Entry), "");
static_assert(sizeof(Entry_1) == sizeof(Entry) + 1 * sizeof(char *), "");
typedef struct {
unsigned len;
char *argv[3];
} Entry_3;
static_assert(alignof(Entry_3) == alignof(Entry), "");
static_assert(sizeof(Entry_3) == sizeof(Entry) + 3 * sizeof(char *), "");
typedef struct {
Entry_1 e1;
Entry_3 e3;
} Entry_init_1_3;
static_assert(offsetof(Entry_init_1_3, e3) == sizeof(Entry_1), "");
const Entry_init_1_3 entry_init_1_3 = {
{ 1, { "a", } },
{ 3, { "b", "c", "d", } },
};
const Entry *const table = (const void *)&entry_init_1_3;
// ^^^^^^^^^^^^^^^ I hope I know what I am doing.
const Entry *const table_end = (const Entry*)((const char*)table + sizeof(entry_init_1_3));
const Entry *table_next(const Entry *it) {
return (const Entry *)(
(const char *)it + sizeof(Entry) + it->len * sizeof(char *)
);
}
int main() {
unsigned entry_idx = 0;
for (const Entry *it = table;
it != table_end;
it = table_next(it), ++entry_idx
) {
for (unsigned x = 0; x < it->len; ++x) {
fprintf(stderr, "table[%d].argv[%d] = %s\n",
entry_idx,
x,
it->argv[x]
);
}
}
}
Code outputs:
table[0].argv[0] = a
table[1].argv[0] = b
table[1].argv[1] = c
table[1].argv[2] = d
Most notable inotify() Linux system call returns an array of flexible array members.
Is there a standard way to statically initialize flexible array members in C?
No.
If not, is there a preferred way?
Don't use flexible array members. Use pointers, as presented in the other answer.
If this is an exam, and you must use a Flexible Array Member, then as I indicated in the comments and as @rici explained in his answer, the purpose of the FAM is to provide a placeholder of a given type that then allows you to allocate storage for the struct itself plus storage for some number of your FAM type in a single-allocation. The advantage this provides is a single-allocation/single-free rather than separate allocation for the struct and then allocating for some number of your needed type.
(prior to the FAM, there was what was referred to as the struct hack where an array of size 1 was used in its place for much the same purpose)
The type is critical to how you deal with and allocate for your FAM. In your case your FAM is item *items[]; (an array of pointers to type item -- Item in your code) So you allocate for the struct and then X number of pointers to item.
To initialize each member of items, you must assign a valid address to a struct of type item (or you can separately allocate, copy to the new block, and then assign the starting address for that block to a pointer in items) In your case, you have an array of struct item called fruits. To assign to items, you must assign the address of each struct to each element in items (remember you have storage for pointers, not storage for struct item -- and you must ensure fruits remains in scope for the duration of your use of basket)
Putting those pieces together, you could do something similar to the following:
Copy#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
typedef struct {
int low, high;
char label[16];
} item;
typedef struct {
size_t length;
item *items[];
} item_coll;
char *find_first_in_range(item_coll *ic, int rlow, int rhigh)
{
for (size_t i = 0; i < ic->length; i++)
if (ic->items[i]->low >= rlow && ic->items[i]->high <= rhigh)
return ic->items[i]->label;
return NULL;
}
int main() {
item fruits[] = {
{10, 20, "Apple"},
{12, 14, "Pear"},
{ 8, 12, "Banana"},
{ 2, 4, "Grape"},
{15, 35, "Watermelon"}
};
size_t nfruits = sizeof fruits/sizeof *fruits; /* avoid magic-numbers */
/* allocate storage for basket + nfruits pointers */
item_coll *basket = malloc (sizeof *basket +
nfruits * sizeof *basket->items);
if (!basket) { /* validate allocation succeeded */
perror ("malloc-basket+5_item_coll");
return 1;
}
basket->length = nfruits; /* assign length */
for (size_t i = 0; i < nfruits; i++) /* assign addresses to structs */
basket->items[i] = &fruits[i];
char *label = find_first_in_range (basket, 12, 15); /* save return */
if (label) /* validate not NULL before printing */
printf ("%s\n", label);
free (basket); /* don't forget to free the memory you allocate */
return 0;
}
(note I have simply used typedefs and removed the struct labels themselves -- it's up to you. Further, you should validate the return from find_first_in_range is not NULL before printing.)
Example Use/Output
Also note I've bracket the high/low range for
Copy$ ./bin/fam_initialization
Pear
Memory Use/Error Check
In any code you write that dynamically allocates memory, you have 2 responsibilities regarding any block of memory allocated: (1) always preserve a pointer to the starting address for the block of memory so, (2) it can be freed when it is no longer needed.
It is imperative that you use a memory error checking program to insure you do not attempt to access memory or write beyond/outside the bounds of your allocated block, attempt to read or base a conditional jump on an uninitialized value, and finally, to confirm that you free all the memory you have allocated.
For Linux valgrind is the normal choice. There are similar memory checkers for every platform. They are all simple to use, just run your program through it.
Copy$ valgrind ./bin/fam_initialization
==6887== Memcheck, a memory error detector
==6887== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==6887== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==6887== Command: ./bin/fam_initialization
==6887==
Pear
==6887==
==6887== HEAP SUMMARY:
==6887== in use at exit: 0 bytes in 0 blocks
==6887== total heap usage: 1 allocs, 1 frees, 48 bytes allocated
==6887==
==6887== All heap blocks were freed -- no leaks are possible
==6887==
==6887== For counts of detected and suppressed errors, rerun with: -v
==6887== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
Always confirm that you have freed all memory you have allocated and that there are no memory errors.
Look things over and let me know if you have further questions.
I strongly suspect you intended this definition:
Copytypedef struct item_coll {
size_t length; Item *items;
} ItemColl;
i.e. a length and an array of Items, not an array of pointers to Item.
As written, the array of pointers does not have any specified length, which makes the struct effectively incomplete; the compiler cannot tell hiw big it is so you can never actually define one. C11 allows this sort of declaration for convenience. The array of unspecified length -- the "flexible array member" -- must come at the very end of the struct and the struct itself cannot be used inside another struct.
You can use a struct with a flexible array member as a kind of base for a concrete dynamically allocated object whose size can be computed as sizeof (Struct) + n * sizeof (ArrayElement). This is occasionally useful as an optimisation and it is legal; all I will say is that its apparent advantages need to be weighed against the code complications it creates.
First, initialing a flexible array member is a non-standard extension. GCC allows it in cases of objects with static storage duration, i.e. those declared at file scope or with the static keyword, as it can set aside the appropriate amount of space for it at compile time.
Regarding why this generates an error:
const HasArray* collection[] = {
&(HasArray){.num_elements = 2, .elements = {51, 17}},
&(HasArray){.num_elements = 4, .elements = {1, 42, 88, 73}}
};
There are ambiguities regarding the storage duration of compound literals prior to C23. In GCC in particular, they always have automatic storage duration. And because GCC only allows flexible array member initialization for objects with static storage duration, you get an error.
The C23 standard clarifies this by stating that the storage duration of a compound literal is that of the enclosing scope. So if you were to compile with GCC 13 or later which has full C23 support, the above would compile cleanly.
In standard C, flexible array members (Are flexible array members valid in C++?) are defined as the last element of a struct with an unspecified size, allowing for dynamic sizing.
This occurs because flexible array members cannot be initialized in this manner. The C standard mandates that structures with FAMs must be dynamically allocated to define the size of the flexible array. Static or compound literal initialization of FAMs is not permitted.
In your initial approach:
HasArray x = {.num_elements = 2, .elements = {51, 17}};
HasArray y = {.num_elements = 4, .elements = {1, 42, 88, 73}};
const HasArray* collection[] = {&x, &y};
Some compilers allow this because x and y are defined with known sizes at compile time (because every time you will have the same size of x and y; it is not dynamic like, for example, a scanf with the size or something like that). However, this usage is non-standard and relies on compiler-specific extensions. The C standard does not define behavior for initializing FAMs in this way, making the code non-portable.
So, to be compliant with the standard, you have to dynamically allocate memory for structures with FAMs:
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
typedef struct {
uint16_t num_elements;
uint8_t elements[];
} HasArray;
int main() {
HasArray *x = malloc(sizeof(HasArray) + 2 * sizeof(uint8_t));
if (x == NULL) {
perror("Failed to allocate memory");
return EXIT_FAILURE;
}
x->num_elements = 2;
x->elements[0] = 51;
x->elements[1] = 17;
HasArray *y = malloc(sizeof(HasArray) + 4 * sizeof(uint8_t));
if (y == NULL) {
perror("Failed to allocate memory");
free(x);
return EXIT_FAILURE;
}
y->num_elements = 4;
y->elements[0] = 1;
y->elements[1] = 42;
y->elements[2] = 88;
y->elements[3] = 73;
const HasArray* collection[] = {x, y};
printf("%d\n", collection[0]->elements[0]);
printf("%d\n", collection[0]->elements[1]);
free(x);
free(y);
return 0;
}
So this will give you:
$ gcc main.c
$ ./a.out
51
17
sizeof ignores the flexible array member because flexible array member takes no space within a structure.
C11-§6.7.2.2/18
As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply. [...]
Note that standard C doesn't permit the flexible array member initialization as in your code. It will invoke undefined behavior (see §6.7.2.2 para 20 and 21). While a GCC permit this as an extension:
GCC allows static initialization of flexible array members. This is equivalent to defining a new structure containing the original structure followed by an array of sufficient size to contain the data.
A flexible array member doesn't count for the size:
... In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.
In addition to the size problem, your code has undefined behavior in C. A flexible array member can't be initialized like that. Gcc might have an extension in that sense, but that isn't portable.