# pragma pack эффект
мне было интересно, может ли кто-нибудь объяснить мне, что такое #pragma pack оператор препроцессора делает, и что более важно, почему его нужно использовать.
Я проверил страница MSDN, который предложил некоторое представление, но я надеялся услышать от людей с опытом. Я видел это в коде раньше, хотя я больше не могу найти, где.
11 ответов:
#pragma packуказывает компилятору упаковать элементы структуры с определенным выравниванием. Большинство компиляторов при объявлении структуры вставляют заполнение между членами, чтобы убедиться, что они выровнены по соответствующим адресам в памяти (обычно кратным размеру типа). Это позволяет избежать снижения производительности (или прямой ошибки) на некоторых архитектурах, связанных с доступом к переменным, которые не выровнены должным образом. Например, учитывая 4-байтовые целые числа и следующие структуру:struct Test { char AA; int BB; char CC; };компилятор может выбрать, чтобы выложить структуру в памяти следующим образом:
| 1 | 2 | 3 | 4 | | AA(1) | pad.................. | | BB(1) | BB(2) | BB(3) | BB(4) | | CC(1) | pad.................. |и
sizeof(Test)будет 4 × 3 = 12, хотя он содержит только 6 байт данных. Наиболее распространенный вариант использования для#pragma(насколько мне известно) - это при работе с аппаратными устройствами, где вам нужно убедиться, что компилятор не вставляет отступы в данные и каждый член следует за предыдущим. С#pragma pack(1), структура выше будет выложена как это:| 1 | | AA(1) | | BB(1) | | BB(2) | | BB(3) | | BB(4) | | CC(1) |и
sizeof(Test)будет 1 × 6 = 6.С
#pragma pack(2), структура выше будет выложена следующим образом:| 1 | 2 | | AA(1) | pad.. | | BB(1) | BB(2) | | BB(3) | BB(4) | | CC(1) | pad.. |и
sizeof(Test)будет 2 × 4 = 8.
#pragmaиспользуется для отправки непереносимых (как только в этом компиляторе) сообщений компилятору. Такие вещи, как отключение определенных предупреждений и структур упаковки, являются распространенными причинами. Отключение определенных предупреждений особенно полезно при компиляции с включенным флагом предупреждения как ошибки.
#pragma packспециально используется для указания на то, что упаковываемая структура не должна иметь выровненных членов. Это полезно, когда у вас есть интерфейс с отображением памяти на часть оборудования и нужно чтобы иметь возможность точно контролировать, куда указывают различные члены структуры. Это, в частности, не очень хорошая оптимизация скорости, так как большинство машин намного быстрее справляются с выровненными данными.
он сообщает компилятору границу для выравнивания объектов в структуре. Например, если у меня есть что-то вроде:
struct foo { char a; int b; };С типичной 32-разрядной машиной вы обычно "хотите" иметь 3 байта заполнения между
aиb, Так чтоbприземляется на 4-байтовую границу, чтобы максимизировать скорость доступа (и это то, что обычно происходит по умолчанию).Если, однако, вы должны соответствовать внешне определенной структуре, которую вы хотите обеспечить компилятору выложите свою структуру точно в соответствии с этим внешним определением. В этом случае, вы можете дать компилятору
#pragma pack(1)сказать не чтобы вставить любое заполнение между членами -- если определение структуры включает заполнение между членами, вы вставляете его явно (например, обычно с членами с именемunusedNилиignoreN, или что-то в этом роде).
#pragma pack (n) просто устанавливает новое выравнивание.#pragma pack () задает выравнивание, которое действовало при запуске компиляции.#pragma pack (push[,n]) помещает текущий параметр выравнивания во внутренний стек, а затем при необходимости устанавливает новое выравнивание.#pragma pack (pop) восстанавливает параметр выравнивания до значения, сохраненного в верхней части внутреннего стека (и удаляет эту запись стека). Обратите внимание, что #pragma pack([n]) не влияет на этот внутренний стек; таким образом, можно иметь #pragma pack(push), за которым следуют несколько экземпляров #pragma pack(n) и завершается одним #pragma pack(pop).примеры:
#pragma pack(push, 1) // exact fit - no padding struct MyStruct { char b; int a; int array[2]; }; #pragma pack(pop) //back to whatever the previous packing mode was Or #pragma pack(1) // exact fit - no padding struct MyStruct { char b; int a; int array[2]; }; #pragma pack() //back to whatever the previous packing mode was Or #pragma pack(1) // exact fit - no padding struct MyStruct { char b; int a; int array[2]; };
элементы данных (например, члены классов и структур) обычно выравниваются по границам WORD или DWORD для процессоров текущего поколения, чтобы улучшить время доступа. Получение DWORD по адресу, который не делится на 4, требует по крайней мере одного дополнительного цикла процессора на 32-разрядном процессоре. Итак, если у вас есть, например, три члена char
char a, b, c;, Они на самом деле, как правило, принимают 6 или 12 байт.
#pragmaпозволяет переопределить это для достижения более эффективного использования пространства, за счет скорости доступа или для обеспечения согласованности данных между различными целями компилятора. Мне было очень весело с этим переходом от 16-битного к 32-битному коду; я ожидаю, что перенос на 64-битный код вызовет такие же головные боли для некоторого кода.
компилятор мая поместите элементы структуры на определенные границы байтов по причинам производительности на определенной архитектуре. Это может оставить неиспользуемое заполнение между участниками. Структура упаковки заставляет элементы быть смежными.
Это может быть важно, например, если вы требуете, чтобы структура соответствовала определенному файлу или формату связи, где данные вам нужны данные, чтобы быть в определенных позициях в последовательности. Однако такое использование не разберитесь с проблемами endian-ness, поэтому, хотя он и используется, он не может быть портативным.
Он также может точно накладывать внутреннюю структуру регистра некоторого устройства ввода-вывода, такого как контроллер UART или USB, например, для того, чтобы доступ к регистру был через структуру, а не прямые адреса.
компилятор может выравнивать элементы в структурах для достижения максимальной производительности на определенной платформе.
#pragma packдиректива позволяет управлять этим выравниванием. Обычно вы должны оставить его по умолчанию для оптимальной производительности. Если вам нужно передать структуру на удаленную машину, вы обычно будете использовать#pragma pack 1для исключения нежелательного выравнивания.
вы, вероятно, захотите использовать это только в том случае, если вы кодируете какое-то оборудование (например, устройство с отображением памяти), которое имеет строгие требования к порядку и выравниванию регистров.
однако, это выглядит как довольно тупой инструмент для достижения этой цели. Лучшим подходом было бы закодировать мини-драйвер в ассемблере и дать ему интерфейс вызова C, а не возиться с этой прагмой.
я использовал его в коде раньше, хотя только для взаимодействия с устаревшим кодом. Это было приложение Mac OS X Cocoa, которое необходимо было загрузить файлы предпочтений из более ранней версии Carbon (которая сама была обратно совместима с оригинальной версией M68k System 6.5...вы поняли идею). Файлы предпочтений в исходной версии были двоичным дампом структуры конфигурации, которая использовала
#pragma pack(1)чтобы не занимать дополнительное пространство и сохранять мусор (т. е. байты заполнения, которые будут в противном случае быть в структуре).оригинальные авторы кода также использовали
#pragma pack(1)для хранения структур, которые использовались в качестве сообщений в межпроцессном взаимодействии. Я думаю, что причина здесь заключалась в том, чтобы избежать возможности неизвестных или измененных размеров заполнения, поскольку код иногда смотрел на определенную часть структуры сообщения, подсчитывая количество байтов с самого начала (ewww).
Я видел, как люди используют его, чтобы убедиться, что структура занимает целую строку кэша, чтобы предотвратить ложное совместное использование в многопоточном контексте. Если у вас будет большое количество объектов, которые будут свободно упакованы по умолчанию, это может сэкономить память и повысить производительность кэша, чтобы упаковать их более плотно, хотя невыровненный доступ к памяти обычно замедляет работу, поэтому может быть недостаток.
обратите внимание, что существуют другие способы достижения согласованности данных, которые предлагает #pragma pack (например, некоторые люди используют #pragma pack(1) для структур, которые должны быть отправлены по сети). Например, см. следующий код и его последующие выходные данные:
#include <stdio.h> struct a { char one; char two[2]; char eight[8]; char four[4]; }; struct b { char one; short two; long int eight; int four; }; int main(int argc, char** argv) { struct a twoa[2] = {}; struct b twob[2] = {}; printf("sizeof(struct a): %i, sizeof(struct b): %i\n", sizeof(struct a), sizeof(struct b)); printf("sizeof(twoa): %i, sizeof(twob): %i\n", sizeof(twoa), sizeof(twob)); }вывод выглядит следующим образом: sizeof (struct a): 15, sizeof(struct b): 24 sizeof(twoa): 30, sizeof (twob): 48
обратите внимание, что размер структуры a точно соответствует количеству байтов, но структура b имеет добавлена прокладка (см. этой для деталей на прокладке). Делая это в отличие от пакета #pragma, вы можете управлять преобразованием "проводного формата" в соответствующие типы. Например," char two[2] "в" short int " и т. д.
Comments