cstructc99c11flexible-array-member

Flexible array member without having to be the last one


I am trying to figure out whether there is a workaround in C to have a flexible array member in a struct(s), that is not the last one. For example, this yields compilation error:

typedef struct __attribute__((__packed__))
{
    uint8_t         slaveAddr;      /*!< The slave address byte */

    uint8_t         data[];         /*!< Modbus frame data (Flexible Array
                                    Member) */
    
    uint16_t        crc;            /*!< Error check value */

} rtuHead_t;

This does not yield an error:

typedef struct __attribute__((__packed__))
{
    uint8_t         slaveAddr;      /*!< The slave address byte */

    uint8_t         data[];         /*!< Modbus frame data (Flexible Array
                                    Member) */

} rtuHead_t;

typedef struct __attribute__((__packed__))
{
    rtuHead_t       head;           /*!< RTU Slave addr + data */

    uint16_t        crc;            /*!< Error check value */

} rtu_t;

But does not work. If I have an array of bytes: data[6] = {1, 2, 3, 4, 5, 6}; and cast it to rtu_t, then crc member will equal 0x0302, not 0x0605.

Is there any way to use the flexible array members in the middle of the struct (or struct in a struct)?


Solution

  • It cannot be done in ISO C. But...

    The GCC has an extension allowing Variably Modified types defined within the structures. So you can define something like this:

    #include <stddef.h>
    #include <stdio.h>
    
    int main() {
        int n = 8, m = 20;
        struct A {
            int a;
            char data1[n];
            int b;
            float data2[m];
            int c;
        } p;
    
        printf("offset(a) = %zi\n", offsetof(struct A, a));
        printf("offset(data1) = %zi\n", offsetof(struct A, data1));
        printf("offset(b) = %zi\n", offsetof(struct A, b));
        printf("offset(data2) = %zi\n", offsetof(struct A, data2));
        printf("offset(c) = %zi\n", offsetof(struct A, c));
        return 0;
    }
    
    

    Except a few warnings about using non-ISO features it compiles fine and produces expected output.

    offset(a) = 0
    offset(data1) = 4
    offset(b) = 12
    offset(data2) = 16
    offset(c) = 96
    

    The issue is that this type can only be defined at block scope thus it cannot be used to pass parameters to other functions.

    However, it could be passed to a nested function, which is yet-another GCC extensions. Example:

    int main() {
       ... same as above
    
        // nested function
        int fun(struct A *a) {
            return a->c;
        }
        return fun(&p);
    }