3
votes

In GCC C, how can I get an array of packed structs, where each entry in the array is aligned?

(FWIW, this is on a PIC32, using the MIPS4000 architecture).

I have this (simplified):

  typedef struct __attribute__((packed))
  {
      uint8_t     frameLength; 
      unsigned    frameType       :3;
      uint8_t     payloadBytes;  
      uint8_t     payload[RADIO_RX_PAYLOAD_WORST];
  } RADIO_PACKET;

RADIO_PACKETs are packed internally.

Then I have RADIO_PACKET_QUEUE, which is a queue of RADIO_PACKETs:

typedef struct
{
    short           read;               // next buffer to read
    short           write;              // next buffer to write
    short           count;              // number of packets stored in q
    short           buffers;            // number of buffers allocated in q
    RADIO_PACKET q[RADIO_RX_PACKET_BUFFERS];
} RADIO_PACKET_QUEUE;

I want each RADIO_PACKET in the array q[] to start at an aligned address (modulo 4 address).

But right now GCC doesn't align them, so I get address exceptions when trying to read q[n] as a word. For example, this gives an exception:

RADIO_PACKET_QUEUE rpq;
int foo = *(int*) &(rpq.q[1]);

Perhaps this is because of the way I declared RADIO_PACKET as packed.

I want each RADIO_PACKET to remain packed internally, but want GCC to add padding as needed after each array element so each RADIO_PACKET starts at an aligned address.

How can I do this?

3
C mandates that arrays consist of contiguous sequences of objects.Kerrek SB
Why do you declare RADIO_PACKET as packed when misalignment is a problem? You do realize that packed structures are not very useful on platforms where misaligned memory access is prohibited as the members of packed structures are not aligned?fuz
@nerdfever.com You could start by making RADIO_PACKET not packed. Do you know what a packed declaration means? (hint: it tells the compiler to leave out any padding).fuz
@nerdfever.com: It seems like "I want padding" and "I want it packed" are two fundamentally incompatible desires. Add your own padding explicitly if you want packing, too.Kerrek SB
you can declare struct that wraps RADIO-PACKET and keep it unaligned. Then you do the array of this new struct.Nick

3 Answers

3
votes

Since you specify that you are using GCC, you should be looking at type attributes. In particular, if you want RADIO_PACKETs to be aligned on 4-byte (or wider) boundaries, then you would use __attribute__((aligned (4))) on the type. When applied to a struct, it describes the alignment of instances of the overall struct, not (directly) the alignment of any individual members, so it is possible to use it together with attribute packed:

typedef struct __attribute__((aligned(4), packed))
{
    uint8_t     frameLength; 
    unsigned    frameType       :3;
    uint8_t     payloadBytes;  
    uint8_t     payload[RADIO_RX_PAYLOAD_WORST];
} RADIO_PACKET;

The packed attribute prevents padding between structure elements, but it does not prevent trailing padding in the structure representation, exactly as is necessary for ensuring the required alignment for every element of an array of the specified type. You should not then need to do anything special in the declaration of RADIO_PACKET_QUEUE.

This is a lot cleaner and clearer than the alternative you came up with, but it is GCC-specific. Since you were GCC-specific already, I don't see that being a problem.

2
votes

You can wrap your packed structure, inside another unaligned structure. Then you do array from this unaligned structures.

Solution 2 could be, to add dummy member char[] at the end of the packed structure. In this case you will need to calculate it somehow, probably manually.

I also will suggest you to rearrange your structure, by placing longer members first and placing uint8_t members last (assuming you have 16/32 bit members and not doing some HW mapping).

1
votes

I think I've solved this, based on the hint from @Nick in the question comments.

I added a RADIO_PACKET_ALIGNED wrapper around RADIO_PACKET. This includes calculated padding.

Then I substituted RADIO_PACKET_ALIGNED for RADIO_PACKET in the RADIO_PACKET_QUEUE structure.

Seems to work:

typedef struct
{
    RADIO_PACKET packet;
    uint8_t padding[3 - (sizeof(RADIO_PACKET) + 3) % 4];
} RADIO_PACKET_ALIGNED;

typedef struct
{
    short           read;               // next buffer to read
    short           write;              // next buffer to write
    short           count;              // number of packets stored in q
    short           buffers;            // number of buffers allocated in q
    RADIO_PACKET_ALIGNED q[RADIO_RX_PACKET_BUFFERS];
} RADIO_PACKET_QUEUE;

Thanks to all the commenters!

Edit: A more portable version of the wrapper would use:

uint8_t padding[(sizeof(int) - 1) - (sizeof(RADIO_PACKET) + (sizeof(int) - 1)) % sizeof(int)];