5

I'm writing array list in C.

I defined an implementation specific struct against a general header.

struct array_list_env {
    void **array;    // pointer to the array
    size_t capacity; // length of the array
    size_t size;     // number of occupied elements in the array
}

Now when I try to change the void **array into void *array[] I got,

error: flexible array member not at end of struct

What is a flexible array member?

Jin Kwon
  • 20,295
  • 14
  • 115
  • 184
  • This is a member of a struct which is an array but does not contain a number of elements. How would the compiler know where to put the other members if it doesn't know the size of your array? Therefore it must be added as last member and you cannot simply use `sizeof(struct array_list_env)` to allocate memory. – Gerhardh Aug 13 '21 at 08:50
  • look at this https://stackoverflow.com/questions/9722632/what-happens-if-i-define-a-0-size-array-in-c-c put you array in the end like this struct array_list_env { size_t capacity; // length of the array size_t size; // number of occupied elements in the array void **array; // pointer to the array } – AdabH Aug 13 '21 at 08:55
  • 1
    You could just type the question "c What is a flexible array member?" in your favourite search engine or look it up in a book. For example https://en.wikipedia.org/wiki/Flexible_array_member – Lundin Aug 13 '21 at 08:56
  • Other than that, `void *array[]` doesn't make sense inside a struct because you cannot declare arrays of incomplete type there. `void**` and `void(*)[]` are only equivalent when the latter decays into the former as part of function parameter list adjustment. But more generally, pointers are not arrays and arrays are not pointers. – Lundin Aug 13 '21 at 08:58
  • 1
    @Lundin: or just mouseover the tag. But `void *` is not incomplete; if you make it a non-FAM array e.g. `void * array[3]` that is perfectly valid. It is different from and incompatible with `void * *` but it is valid. And `void * []` decays to `void * *` but `void (*) []` _is_ (pointer to) array of incomplete and a constraint violation. – dave_thompson_085 Aug 13 '21 at 09:12
  • It's just a typo, I meant to type `void*(*)[]`. That is, a pointer to an incomplete array type `void *array[]`. Incomplete not because of void type but because the array size is missing. – Lundin Aug 13 '21 at 09:15

1 Answers1

14

A flexible array member is an array without a specified size. The member has to be the last member of the struct. The actual array size is set when you allocate memory for the struct. Consequently, it only makes sense together with dynamic allocation.

Example:

#define ARRSIZE 10

struct FAM
{
    size_t sz;
    int arr[];   // Must be last struct member
};

struct FAM* fam = malloc(sizeof(struct FAM) + ARRSIZE * sizeof(int));

Now fam->arr is an array with ARRSIZE elements that you can access ussing fam->arr[index].

Further code like:

struct FAM* famA = malloc(sizeof(struct FAM) + 10 * sizeof(int));
struct FAM* famB = malloc(sizeof(struct FAM) + 1000 * sizeof(int));

will give you two pointers of the same type even though the size of the arrays differ.

So why would I use it?

Look at this code

struct FAM
{
    size_t sz;
    int arr[];
};

struct P
{
    size_t sz;
    int* p;
};

int getFAM(struct FAM* f, unsigned long x)
{
    return f->arr[x];
}

int getP(struct P* p, unsigned long x)
{
    return p->p[x];
}

It's two ways of doing "the same". One using a flexible array member, the other using a pointer.

Compiled with gcc -O2 on https://godbolt.org/ I get

enter image description here

which indicates that the flexible array can save you one indirection and thereby generate faster code.

It can be described like: In case of a flexible array member the array has fixed offset from a struct pointer while in case of a pointer-to-int member the array is located where the pointer value says. Consequently, in the first case the compiler can use that "known fixed offset" directly but in the second case the compiler must read the pointer value first and then read the array data.

Note: This example is for one specific system (aka compiler/cpu type). On other systems the result may differ.

Support Ukraine
  • 42,271
  • 4
  • 38
  • 63