2015-11-09 6 views
2

В GCC C, как я могу получить массив упакованных структур, где каждая запись в массиве выровнена?Как получить выровненный массив упакованных структур в GCC C?

(FWIW, это на PIC32, используя архитектуру MIPS4000).

У меня есть это (упрощенный):

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

RADIO_PACKETs упакованы внутри.

Тогда я RADIO_PACKET_QUEUE, который представляет собой очередь 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; 

Хочу каждый RADIO_PACKET в массиве д [], чтобы начать в выровненном адресе (по модулю 4 адреса).

Но сейчас GCC не выравнивает их, поэтому я получаю исключения адреса при попытке прочитать q [n] в качестве слова. Например, это дает исключение:

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

Возможно, это из-за того, как я объявил RADIO_PACKET в упакованном виде.

Я хочу, чтобы каждый RADIO_PACKET оставался упакованным внутри, но хотел бы, чтобы GCC добавлял отступы по мере необходимости после каждого элемента массива, чтобы каждый RADIO_PACKET начинался с выровненного адреса.

Как я могу это сделать?

+4

C мандаты, что массивы состоят из * смежных * последовательностей объектов. –

+0

Почему вы объявляете 'RADIO_PACKET' как упакованную, когда проблема несоосности является проблемой? Вы понимаете, что упакованные структуры не очень полезны на платформах, где запрещен неправильный доступ к памяти, поскольку элементы упакованных структур не выровнены? – fuz

+0

'* (int *) & (все, что не является int)' провоцирует неопределенное поведение и * будет * ошибочно оптимизировано текущими компиляторами. – zwol

ответ

2

Поскольку вы указали, что вы используете GCC, вы должны смотреть на атрибуты типа. В частности, если вы хотите, чтобы RADIO_PACKET s были выровнены по 4-байтовым (или более широким) границам, вы должны использовать __attribute__((aligned (4))) по типу.При нанесении на struct, он описывает выравнивание экземпляров общей struct, а не (непосредственно) выравнивание любых отдельных членов, поэтому можно использовать его вместе с атрибутом 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; 

атрибут packed предотвращает прокладку между элементами структуры, но делает это не предотвращает прослеживание заполнения в представлении структуры, точно так же, как это необходимо для обеспечения необходимого выравнивания для каждого элемента массива указанного типа. Тогда вам не следует делать что-либо особенное в объявлении RADIO_PACKET_QUEUE.

Это намного чище и понятнее, чем альтернатива, с которой вы столкнулись, но она специфична для GCC. Поскольку вы уже были GCC-специфичными, я не вижу в этом проблемы.

+0

Гораздо лучше, чем мой ответ. Именно этого я и искал. –

2

Вы можете обернуть свою упакованную структуру внутри другой неуравновешенной структуры. Затем вы выполняете массив из этих неуравновешенных структур.

Решение 2 может быть, чтобы добавить фиктивный элемент char [] в конце упакованной структуры. В этом случае вам нужно будет как-то вычислить его, возможно, вручную.

Я также предлагаю вам перестроить вашу структуру, разместив более длинные элементы и разместив последние uint8_t членов (при условии, что у вас есть 16/32 бит элементов, а не выполнение некоторого HW-отображения).

+0

Thanks; upvoted. Мой собственный ответ - это просто реализация этого. –

1

Я думаю, что я решил это, основываясь на подсказке @Nick в комментариях к вопросу.

Я добавил RADIO_PACKET_ALIGNED оболочку вокруг RADIO_PACKET. Сюда входит вычисленное дополнение.

Затем я заменил RADIO_PACKET_ALIGNED для RADIO_PACKET в структуре RADIO_PACKET_QUEUE.

Кажется, работает:

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; 

Благодаря всем комментаторам!

Edit: более портативная версия оболочки будет использовать:

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