Использование директивы компилятора #pragma pack(1) во встроенных приложениях

Недавно я наткнулся на эту директиву препроцессора #pragma pack(1) и задался вопросом, почему она используется?

Я погуглил об использовании и обнаружил, что у него есть другие параметры, такие как push, pop и т. Д. Кто-нибудь использовал это в своем встроенном приложении?

Я хотел бы узнать несколько примеров того, как/почему вы использовали эту директиву и на каком типе процессора? Каковы плюсы/минусы использования этой директивы?

Спасибо.

Просто имейте в виду: прагмы по определению нестандартны, а доступные прагмы и их использование различаются в зависимости от того, какой компилятор вы используете. Уроки, извлеченные для этого конкретного приложения, НЕ обязательно будут перенесены в другую ситуацию.

Ответы (3)

#pragma pack(1)гарантирует, что structэлементы C упакованы по порядку и на границах байтов. Это может сэкономить оперативную память, которая часто бывает ценна в микроконтроллере.

Упакованные структуры также позволяют выполнять приведение типов непосредственно к буферам памяти для обмена данными:

void f(void *buf)
{
  struct ip_header *hdr = (struct ip_header *)buf;
  hdr->dst = 0x8000001;
}

Будьте осторожны, где вы используете #pragma pack. Он имеет глобальную область действия (как и в препроцессоре), поэтому если вы забудете его отключить, это повлияет на любые #includeфайлы. Если вы хотите упаковать только определенные структуры, рассмотрите возможность использования GCC __attribute__ ((packed)).

Однако из-за проблем с выравниванием упакованные структуры могут повлиять на производительность. Например:

При упаковке в байты:

struct
{
  uint8_t  a;
  uint32_t b;
  uint8_t  c;
  uint8_t  d:
};

Будет храниться как (без учета порядка байтов):

а, б0, б1, б2, б3, в, г

Однако многие архитектуры требуют, чтобы 32-битные обращения были согласованы с 32-битными адресами. С упакованной структурой машине придется сделать несколько обращений к байтам, а затем снова сшить их вместе.

Столкнувшись с вышеизложенным structбез включения упаковки, компилятор может реорганизовать его как:

б0, б1, б2, б3, а, в, г

В вашем последнем примере без включенной упаковки и 32-битного выравнивания по умолчанию некоторые компиляторы могут вместо этого добавить дополнительные байты в структуру, чтобы она отображалась в памяти как a, x, x, x, b0, b1, b2, b3 , c, x, x, x, d, где x — байт заполнения. Возможны другие вариации.
Спасибо за объяснение, Джоби и tcrosley. Я наконец понял!
Очень важно отключить его, установив для него значение 0 после того, как вы закончите. Невыполнение этого требования приведет к странному поведению.
Компиляторам не разрешено изменять порядок полей в структурах C. Вы можете изменить порядок, чтобы поставить b на первое место самостоятельно.

Еще одна причина упаковывать структуры на границах байтов — обеспечить выравнивание членов при передаче данных между разными процессорами.

Мне часто приходится передавать структуры данных между MCU и приложением хост-компьютера. ПК будет упаковывать структуры на 32-битных границах, если только не будет указано упаковывать их на 1-байтовые границы. Микроконтроллер PIC24F будет упаковывать структуры по 16-битным границам, если не будет указано упаковывать их по 1-байтовым границам.

Поручая им обоим упаковывать свои структуры на границах в 1 байт, он гарантирует, что данные находятся в одном и том же месте при доступе к ним на любом конце. Без него вам пришлось бы дополнять структуры резервными байтами, чтобы члены данных правильно выровнялись.

Традиционное использование, где я видел, это использование для чтения информации из файлов. Например, вы можете определить структуру, элементы которой соответствуют элементам заголовка BMP-файла, а затем прочитать весь заголовок за одну операцию быстрого чтения. Итак, BMP может быть не лучшим примером (его заголовок не имеет проблем с выравниванием в 32-битных системах), но вы поняли идею. Я полагаю, что это столь же полезно во встраиваемом мире.

Действительно. Вы можете наложить структуру на входящий пакет (или использовать ее для создания исходящего пакета), на набор отображаемых в память регистров или построить пакет журнала, во всех случаях, когда упаковка потенциально может отличаться от оптимального выравнивания ЦП для доступа к памяти. .
Совершенно верно, но остерегайтесь порядка байтов. Когда многобайтовые сущности сопоставляются со структурой, может потребоваться перестановка по порядку байтов. Например, IP-пакеты упакованы с прямым порядком байтов, а x86 и (обычно) ARM — с обратным порядком байтов.