Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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)?

like image 864
Łukasz Przeniosło Avatar asked Oct 27 '25 12:10

Łukasz Przeniosło


2 Answers

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);
}
like image 69
tstanisl Avatar answered Oct 30 '25 03:10

tstanisl


A flexible array member must be the last member of the struct, and a struct containing a flexible array member may not be a member of an array or another struct.

The intended use of such a struct is to allocate it dynamically, putting aside enough space for the other members plus 0 or more elements of the flexible member.

What you're attempting to do is overlay a struct onto a memory buffer that contains packet data that you want to parse simply by accessing the members. That's not possible in this case, and in general doing so is not a good idea due to alignment and padding issues.

The proper way to do what you want is to write a function that deserializes the packet one field at a time and places the result in a user-defined structure.

like image 26
dbush Avatar answered Oct 30 '25 03:10

dbush