2017-02-05 20 views
3

Я читаю этот код для 32-битного MCU.Нужно использовать выравнивание структуры данных

Мой вопрос: нужно ли использовать __attribute__((packed, aligned(1)))? Это просто хорошая практика или она вызывает непредсказуемое поведение без нее.

typedef struct __attribute__((packed, aligned(1))) radio_packet { 
    uint8_t tag;    /* 1 byte */ 
    uint32_t id;    /* 4 bytes */ 
    uint16_t size;    /* 2 bytes */ 
    uint16_t element;   /* 2 bytes */ 
    uint8_t length;   /* 1 byte */ 
    // The items above comprise HDR_SIZ 
    uint8_t data[PKT_SIZ]; 

} radio_packet_t; 
+1

Ну, если вы хотите избежать добавления, добавленного компилятором (между 'tag' и' id'), тогда вам нужно «упаковать» эту структуру. –

+0

Вообще говоря, эти атрибуты не используются; они предназначены для специализированных ситуаций. Но в этих особых ситуациях они совершенно необходимы, и похоже, что вы используете этот тип в одной такой ситуации. – Hurkyl

+0

'__attribute __ ((упакованный))' достаточно, 'aligned (1)' избыточно. Однако, чтобы уменьшить выравнивание по отношению к чему-то еще, чем к 1, оба они необходимы. Например, с учетом структуры с естественным выравниванием 4, '__attribute __ ((упакованный, выровненный (2)))' уменьшается выравнивание до 2. «упакованный» необходим в этом случае, потому что выравнивание в противном случае не может быть уменьшено. (Это относится к GCC.) –

ответ

2

Это просто хорошая практика или это вызывает непредсказуемое поведение без нее?

Ни один из двух. Если данные, будучи упакованными или нет, используются только внутри программы, тогда хорошо, чтобы компилятор решил, как упорядочить эти данные и как их выровнять, - если вы действительно не хотите сохранять ram.

Если данные должны иметь определенный формат - и я думаю, что да, потому что ваша структура называется «radio_packet», то вы хотите быть уверены, что формат, который вы пишете, соблюдается. В этом случае вы хотите упаковать данные и вставить вручную прописку, если требуется формат.

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

Дальнейшее продление. Хорошая практика не использоватьупаковка и/или выравнивание, в нормальных ситуациях, потому что компилятор знает лучше, чем человек, что лучше. Вы используете упаковку/выравнивание, когда знаете, что делать лучше, чем компилятор, например, потому что вы должны уважать какой-то формат, о котором компилятор не знает. Обычно (но зависит от компилятора), он делает выбор, чтобы оптимизировать производительность, возможно, растрачивая некоторый баран. Для достижения максимальной производительности он может выровнять поля с предпочтительным выравниванием ЦП, вставить неиспользуемые/скрытые поля или даже переупорядочить их. Это не очень хорошо, если вы затем принимаете этот пакет и отправляете его на какое-то оборудование или программное обеспечение, которое не использует одно и то же дополнение/выравнивание: 32-битный процессор предпочитает 32-разрядные согласованные данные, но 8-битный ЦП не заботится.

С точки зрения вашей программы/ЦП в любом случае никогда не будет непредсказуемого поведения. Компилятор прекрасно знает, что он делает, даже если вы навязываете ему некоторые ограничения. Неправильное или [вероятно, не так] непредсказуемое поведение происходит, когда данные, неправильно отредактированные, входят/выходят из вашей программы.

+0

спасибо, что это отличный ответ. – scalauser

0

Это не обязательно, но вы можете сделать предположения в коде позже. Например, вы можете сделать безопасную memcpy из массивов char в radio_packet_t, и не все компоненты в программе должны знать тип radio_packet_t и могут просто использовать байтовые буферы для отправки radio_packet_t по сети или в файл или что-то еще.