How to override C compiler aligning word-sized variable in struct to word boundary

1.8k views Asked by At

I have a structure specified as following

  • Member 1, 16 bits
  • Member 2, 32 bits
  • Member 3, 32 bits

which I shall be reading from a file. I want to read straight from the file into the struct.

The problem is that the C compiler will align the variables m1, m2 and m3 to word boundaries which are at 32 bits since I am working on an ARM Cortex M3 for the following struct declaration:

typedef struct
{
    uint16_t m1;
    uint32_t m2;
    uint32_t m3;
}something;

Reading directly from file will put wrong values in m2 and m3, and reads 2 extra bytes too.

I have hacked around and am currently using the following which works just fine:

typedef struct
{
    uint16_t m1;
    struct
    {
        uint16_t lo;
        uint16_t hi;
    }m2;
    struct
    {
        uint16_t lo;
        uint16_t hi;
    }m3;
}something;

However, this looks like a really dirty hack. I cannot help wishing for a cleaner way to force the compiler to put halves of m2 and m3 in different words, however sub-optimal it may be.

I am using arm-none-eabi-gcc. I know about bit packing, but am unable to work around this optimisation.

Edit: Turns out I didn't know enough about bit-packing :D

4

There are 4 answers

0
missimer On BEST ANSWER

What you are looking for is the packed attribute. This will force gcc to not do any padding around members. Taken from the GCC Online docs:

packed

This attribute, attached to an enum, struct, or union type definition, specified that the minimum required memory be used to represent the type. Specifying this attribute for struct and union types is equivalent to specifying the packed attribute on each of the structure or union members. Specifying the -fshort-enums flag on the line is equivalent to specifying the packed attribute on all enum definitions.

You may only specify this attribute after a closing curly brace on an enum definition, not in a typedef declaration, unless that declaration also contains the definition of the enum.

So what you want is something like:

typedef struct
{
    uint16_t m1;
    uint32_t m2;
    uint32_t m3;
} __attribute__ ((packed)) something;

In addition I would recommend using a compile time assertion check to ensure that the size of the struct is really what you want it to be.

0
AShelly On

Perhaps #pragma pack(2). That should force the compiler to use 2-byte alignment

1
resultsway On
__attribute__ ((aligned (2)));
1
Serge Ballesta On

You cannot directly read such a struct from a file, and you should never try to. Misalignement can cause traps on certain architecture, and you should not rely on pragma to fix that.

The almost(*) portable way if to read file elements into struct elements unless your are sure that the struct was written with same architecture and alignement (at least compatible) as your are using for reading.

So for your use case, I would recommend :

fread(&something.m1, sizeof(something.m1), 1, fd);
fread(&something.m2, sizeof(something.m2), 1, fd);
fread(&something.m3, sizeof(something.m3), 1, fd);

(*) it is almost portable because it assume that there are no endian problems which can be correct or not depending on your needs. If you are on one single machine or one single architecture it is fine, but if you write struct on a big endian machine and read it on a little endian one, bad things will occur ...