8

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?

7
  • 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. Commented Aug 13, 2021 at 8:50
  • look at this stackoverflow.com/questions/9722632/… 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 } Commented Aug 13, 2021 at 8: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 en.wikipedia.org/wiki/Flexible_array_member Commented Aug 13, 2021 at 8: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. Commented Aug 13, 2021 at 8: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. Commented Aug 13, 2021 at 9:12

1 Answer 1

25

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.