Когда мы не должны использовать пакет #pragma?

В C, когда мы используем структуры, когда было бы нецелесообразно использовать директиву #pragma pack..?

дополнение к вопросу.....

Может кто-нибудь, пожалуйста, объясните, как может произойти сбой при доступе к неуравновешенным данным с помощью указателя?

Ответы

Ответ 1

Разработчик прошивки здесь. #pragma pack очень знакомая территория. Я объясню.

В общем случае вы не должны использовать #pragma pack. Да, это уменьшит ваши структуры в памяти, поскольку устраняет все дополнения между элементами структуры. Но это может сделать доступ к тем членам намного дороже, так как члены могут больше не уклоняться от требуемого выравнивания. Например, в ARM-архитектурах 4-байтовые ints обычно должны быть выровнены по 4 байт, но в упакованной структуре они могут отсутствовать. Это означает, что компилятор должен добавить дополнительные инструкции для безопасного доступа к этому члену структуры, или разработчик должен получить к нему доступ побайтно и восстановить его вручную. В любом случае это приводит к большему количеству кода, чем к выровненному доступу, поэтому ваша структура заканчивается меньше, но ваш код доступа потенциально заканчивается медленнее и больше.

Вы должны использовать #pragma pack, когда ваша структура должна соответствовать формату данных exact. Обычно это происходит, когда вы пишете код, соответствующий спецификации передачи данных или доступа... например, сетевым протоколам, протоколам хранения данных, драйверам устройств, которые получают доступ к регистрам HW. В таких случаях вам может понадобиться #pragma pack, чтобы ваши структуры соответствовали спецификации, определенной макетом данных. Это может повлечь за собой такое же снижение производительности, упомянутое в предыдущем абзаце, но может быть единственным способом выполнить спецификацию.

Ответ 2

Я бы сказал, что вы не должны упаковывать, если не существует действительно веской причины для этого.

Когда пакет указан, все отступы удаляются. Поэтому члены структуры могут быть неравнозначными, что может иметь последствия для производительности.

Ответ 3

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

Это означает, что если у вас есть 32-битное значение, вы можете получить к нему доступ эффективно, если он хранится по адресу, который может быть разделен на четыре.

Если вы используете #pragma pack, расположение переменной может быть любым, и компилятор должен получить доступ к элементу по частям и объединить их вместе. Конкретно, ниже приведенный код читает нормальный int на V850E (популярном микроконтоллере во встроенном мире):

LD.W        a[zero],r5

Соответственно, следующий код для доступа к int в упакованной структуре:

LD.BU       g+3[zero],r1
SHL         8,r1
LD.BU       g+2[zero],r6
OR          r1,r6
SHL         8,r6
LD.BU       g+1[zero],r7
OR          r6,r7
SHL         8,r7
LD.BU       g[zero],r1
OR          r7,r1

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

Я бы настоятельно рекомендовал, чтобы вы вообще не использовали '#pragma pack', если это не было абсолютно необходимо. Если у вас есть контроль над определением структуры, есть методы, чтобы убедиться, что макет структуры не содержит пробелов. Если нет, лучшим подходом было бы скопировать любые неизменные данные в новую, выровненную структуру и использовать ее в вашем приложении.

Ответ 4

Директива pack #pragma предлагает способ выполнить это требование. Эта директива определяет выравнивание упаковки для элементов структуры. Прагма вступает в силу при объявлении первой структуры после просмотра pragma. Компилятор Turbo C/С++ не поддерживает эту функцию, компилятор VС++ делает.

Ответ 5

Вы не должны использовать #pragma pack или подобное. Это всегда приводит к опасным проблемам с переносимостью. Например, рассмотрим struct:

struct foo {
    char a;
    int b;
} bar;

и вызовите scanf("%d", &bar.b). На машинах без несогласованного доступа это приведет к ошибке (или повреждению памяти!) Внутри scanf! Это связано с тем, что &bar.b на самом деле не является допустимым int * - он смещен, но код, который он передал, не может знать и работать вокруг него, как компилятор, если бы вы только что написали bar.b = 42;.

Единственным предназначением для упакованных структур является сериализация, но это также приводит к непереносимым файлам. Вы должны просто написать правильные функции сериализации.