Проблема
Я работаю над отправкой исходной структуры по сети известной программе с другой стороны, но вам нужно беспокоиться о незаметно введенной памяти, используемой для выравнивания структур (рассматриваются другие проблемы, такие как endianness). То, с чем я работаю, это что-то вроде:
typedef struct __attribute__((packed))
{
uint16_t field1;
uint16_t field2;
uint16_t field3;
} packed_1_s;
typedef struct __attribute__((packed))
{
uint16_t fieldA;
uint16_t fieldB;
packed_1_s structArray[10];
} packed_2_s;
typedef struct __attribute__((packed))
{
uint16_t fieldX;
packed_2_s fieldY;
uint8_t arrayZ[20];
} data_s;
Я понимаю, что обычно структура packages_1_s могла/имела бы дополнительное пространство, выделенное для каждого экземпляра структуры, чтобы заполнить его до размера, соответствующего предпочтениям компилятора (в зависимости от оборудования, для которого он строится), и что предпочтительный размер может быть где угодно от 2 байтов до 64 байтов (совсем недавно). Обычно, если бы у меня был единственный экземпляр упакованных_____ в pack_2_s, проблем не было бы, но мне дано понять некоторые отличия, когда вы пытаетесь поместить элементы в массив.
Попытки решения
Документация gcc, похоже, предполагает, что, просто включив упакованный атрибут в определение pack_2_s, поля, даже если они являются массивами, будут как можно более плотно упакованы и не будут добавлять пространство в структуру упакованных_2_s, чтобы выровняйте элементы массива. Документация по атрибуту align(), однако, предполагает, что массивы обрабатываются иначе, чем другие поля, и для этого необходимо, чтобы атрибут align/pack был установлен в поле непосредственно для его изменения дополнительного интервала, добавленного для соответствия указанному выравниванию (или его отсутствию). Я попытался установить упакованный атрибут в поле structArray, и когда это не сработало, выполнил тест, установив упакованный атрибут на arrayZ в приведенном выше коде:
packed_1_s structArray[10] __attribute__((packed));
uint8_t arrayZ[20] __attribute__((packed));
Обе попытки дали мне компилятор, предупреждающий, что упакованный атрибут не был понят в этом контексте и будет пропущен (хорошая вещь, которую я создаю с помощью "-Wall" ).
Я надеялся, что способ обойти проблему будет заключаться в использовании атрибута align (1), указывающего желаемое выравнивание 1 байта, которое сопоставимо с упакованным атрибутом, но документация говорит, что атрибут align() может только увеличить выравнивание и упакованные должны использоваться для уменьшения выравнивания.
Вопросы
Из того, что я могу определить из документации GCC, кажется, что в нее вставлены 3 основных случая добавления дополнительной памяти.
- Содержимое структуры может иметь дополнительную память, выделенную для сама структура, чтобы изменить расстояние между полями.
Эффективно определение карты памяти структуры содержимое внутри структуры может измениться (хотя и не порядок элементов).- Структуры могут иметь дополнительную память, выделенную им для заполнения их до более эффективного общего размера. Это обычно предназначенные для других переменных, следующих после объявления одной из их экземпляры не попадают в один и тот же "блок", экземпляр структуры, где "блок" определяется системой/компилятором.
- Массивы, будь то внутри структуры или нет, могут иметь дополнительные к ним добавлена память, чтобы сместить элементы в эффективное выравнивание.
Насколько я могу судить, упакованный атрибут может использоваться, чтобы влиять на структуры и блокировать дополнительную память, добавленную в случаях 1 и 2 выше, но, похоже, нет способа обработать случай 3 выше на моем компилятор (ы).
Вопрос
Есть ли способ гарантировать, что структура data_s не будет иметь абсолютно никакого дополнительного пространства, добавленного к нему или любой из его подструктур, чтобы у меня не было зависимых от компилятора сдвигов на карте памяти? Неужели я не понимаю случаи, когда компилятор может вставить пространство, чтобы преднамеренно сдвинуть карту памяти?
ИЗМЕНИТЬ
Я обсуждал некоторые проблемы с моим местным гуру, и это звучит так, как будто я неправильно понимаю случай 3. Элементы в массиве не имеют места, вставленного между ними, но дополнительное пространство, чтобы гарантировать правильность их выравнивания, добавляется к самой структуре. По-видимому, это говорит о том, что такие вещи, как "sizeof (structureOnlyContaining_uint32_t)", не всегда будут возвращать "4", поскольку дополнительное пространство может быть добавлено для выравнивания типа данных uint32_t в используемом компиляторе. В результате действительно есть только 2 случая:
- Большие смещения между полями на карте памяти структуры.
Пространство между полями может быть изменено для выравнивания каждого поля. Это можно изменить с помощью атрибутов упакованных или align().- Отступ по структуре. Размер для структуры, возвращаемый sizeof(), можно модифицировать так, чтобы массивы структур заканчивались правильно выровнен для системы. Это позволяет всем системам принимать что начало структур всегда будет выровнено, что вызовет проблемы если они не являются. На это не влияет атрибут pack или align.
Из-за нового случая 2 элементы массива в структуре необязательно подчиняются атрибутам упакованного или align(), указанным в структуре, хотя начало массива и поле, непосредственно следуя за массивом.
Теперь мой вопрос о том, как обращаться с structArray в пакетах_______________ __ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ поскольку размер массива в целом не может быть гарантирован чисто упакованным атрибутом. Есть ли способ гарантировать фиксированный размер поля structArray в целом? Следует отметить, что я не могу увеличить размер упакованных_1_s слишком сильно, так как структура data_s должна быть как можно меньше (ее замена аудио/видео данных в сценарии потоковой передачи).