Search code examples
cflexible-array-member

What is a flexible array member in a struct?


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?


Solution

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