Что задает директива pragma pack в C++
Что проверяют
Понимаете ли вы, зачем компилятор вставляет отступы между полями и чем платят за их отключение. Вопрос стоит на границе языка и железа, поэтому за определением почти всегда идут вопросы про выравнивание, размер структуры и последствия для доступа к памяти.
Как отвечать
#pragma pack(N) ограничивает выравнивание полей структуры сверху: каждое поле выравнивается по меньшему из двух значений, своего естественного выравнивания и N. Отсюда исчезают отступы между полями и добивка в конце, а размер структуры уменьшается.
#pragma pack(push, 1)
struct Packed { char a; int b; short c; }; // 7 байт
#pragma pack(pop)
struct Plain { char a; int b; short c; }; // 12 байтБез директивы int кладётся по адресу, кратному четырём, поэтому после a появляются три байта отступа, а в конце структура добивается до кратности своему выравниванию. Пара push и pop обязательна: иначе настройка протечёт на весь остальной код единицы трансляции, включая заголовки, подключённые ниже.
Скажите и про область применения: разбор бинарных форматов, сетевых пакетов, обмен с устройствами, где раскладка задана протоколом. Надёжнее, впрочем, собирать такие данные явным чтением полей из буфера, тогда под контролем окажется ещё и порядок байтов.
Где спотыкаются
Называют директиву частью стандарта. Это расширение компилятора, у gcc и clang для того же есть атрибут упаковки, а стандартный alignas умеет только усиливать выравнивание, ослабить его им нельзя.
Берут адрес или ссылку на невыровненное поле упакованной структуры. На x86 такой доступ просто медленнее, на ряде процессоров ARM и в векторных инструкциях это аппаратное исключение, а с точки зрения языка обращение через невыровненный указатель это неопределённое поведение.
Считают, что упаковка делает структуру переносимой. Размер int, порядок байтов и знаковость char от директивы не меняются, поэтому структура, записанная как есть в файл на одной платформе, на другой читается неверно.