2016-12-22 5 views
0

Я хотел бы создать массив различных структур с разными размерами.Упаковка объединения структур

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

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

Результатом является дерево дескрипторов конфигурации USB, каждый дескриптор упакован сразу после последнего, чтобы создать единую конфигурацию blob. Будут приветствоваться предложения о различных подходах к проблеме. http://www.beyondlogic.org/usbnutshell/usb5.shtml#ConfigurationDescriptors

struct a { 
    uint16_t some_field; 
}; 
struct b { 
    uint32_t another_field; 
}; 
union detail { 
    struct a a; 
    struct b b; 
}; 
const union detail configuration[] = { 
    { .a = { .some_field = 23 } }, 
    { .b = { .another_field = 12 } } 
}; 

В приведенном выше примере это значительно упрощенной версии моей текущей, неисправная, попытка. Каждый элемент массива имеет размер самого большого члена объединения. Таким образом, каждый элемент массива имеет 32 бита, а первая запись дополняется нулями.

Выходной ток 1700 0000 0c00 0000

Желаемая выход 1700 0c00 0000

Существующие методы получения этого упаковано вывода использовать гигантский массив Uint8 с макросами, чтобы вставить более сложные значения, такие как 16-битных чисел. Массив структур более точно представляет данные и обеспечивает безопасность типов, если он будет работать.

Мне не нужно иметь возможность индексировать или получать доступ к данным из массива, а blob - в низкоуровневые подпрограммы USB. Игра с атрибутом gcc упакована не изменила поведение стандартного объединения.

+1

Эта проблема является одной из причин, почему структуры/объединения не должны использоваться для сериализации. – user694733

+6

Союзы не работают. – 2501

+0

Спасибо за идеи о наличии одной большой структуры. К сожалению, полная структура зависит от конфигурации, одна конфигурация может иметь 1 конечную точку, другая может иметь три, где каждая конечная точка является другой структурой. GCC не поддерживает вложенные структуры с массивами переменной длины и делает их фиксированным размером побеждает повторное использование. – lod

ответ

0

Принимая комментарии от @ Basile-Starynkevitch, @ Jonathan-Leffler и других, что то, на что я надеялся, не может быть сделано, я передумал. Я действительно требовал точно контролировать относительное размещение структур в памяти/вспышке. Размещение выполняется с помощью компоновщика, и в итоге я нашел там решение.

Во-первых, внутри секции SECTIONS скрипта компоновщика я создал специальный блок. Единственный способ обеспечить заказ - создать несколько разделов и вручную заказать их, cpack0-3 в этом случае.

.text : ALIGN(4) /* Align the start of the block */ 
{ 
    *(.cpack0) *(.cpack1) *(.cpack2) *(.cpack3) 
} > MFlash32 

Затем структурные переменные помещаются в специальные разделы. Длительный синтаксис, повторяющийся, может быть упрощен элементами #define в реальной реализации.

const struct a configuration __attribute__((section(".cpack0"), aligned(1))) = { 
    .some_field = 23 
}; 

const struct b configuration1 __attribute__((section(".cpack1"), aligned(1))) = { 
    .another_field = 12 
}; 

Таким образом, мы имеем переменную конфигурации, выровненный по адресу 4 байта для приятного доступа и определяется с помощью-структуру для безопасности типа. Последующие части конфигурации также определяются структурами для обеспечения безопасности и последовательно помещаются в память. Атрибут aligned(1) гарантирует, что они плотно упакованы без пустого пространства.

Это решает мою проблему, определение конфигурации выполняется через структуру, для которой предоставляются все преимущества, уродство скрыто #define, а окончательная конфигурация представляет собой двоичный блок с переменной длиной, доступный указателем uint8_t*.По мере увеличения указателя он легко перемещается по различным элементам конфигурации.

2

Я хотел бы создать массив различных структур с различными размерами.

Это просто невозможно в C (и по уважительным причинам). Массив (в C) выполнен из компонентов того же размера (и типа). Если бы это было не так, индексированный доступ к элементу этого массива был бы очень сложной и трудоемкой операцией (что противоречит духу C, однако на C++ вы можете определить свой собственный operator []).

Вы могли бы вместо того, чтобы иметь массив char -s (например, const char data[] = {0x35, 0x27, 0}; и т.д., возможно, что большой массив байт может быть сгенерирован некоторой одноранговой сценарий излучающих некоторые C код инициализации большого массива) и иметь некоторую parsing процедуры для процесса Это. Или вы могли бы иметь массив указателей :

union detail { 
    struct a* aptr; 
    struct b* bptr; 
}; 

static const struct a firstelem= {.some_field= 35}; 
static const struct b secondelem= {.another_field= 12}; 
const union detail configuration[] = { 
    {.aptr= &firstelem}, 
    {.bptr= &secondelem}, 
}; 

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

0

Вы не должны использовать union для этого, он не делает то, что вы думаете. Если вам нужен массив структур, где каждая структура может быть другого типа, это невозможно. Вместо этого вам нужно будет определить «суперструктуру», содержащую все остальные структуры, в правильном порядке.

Однако это не решает проблему с выравниванием/дополнением. Чтобы отключить отступы в структурах (и объединениях), вы должны прибегнуть к нестандартным C. Общим нестандартным расширением является #pragma pack. Компилятор gcc также поддерживает нестандартный атрибут «упакованный», см. What is the meaning of “attribute((packed, aligned(4))) ”.Поскольку код, который отключает заполнение, является нестандартным, он также не переносится.

Также можно решить проблему, создав массив uint8_t, а затем прочитайте куски данных в этот массив. Это называется сериализации/де-сериализации данных. Конверсии с любого типа указателя на uint8_t* или типы символов безопасны, но, к сожалению, другой путь вызывает вызовы неопределенного поведения. Это из-за ошибки на языке C, которая часто упоминается как "the strict aliasing rule", что иногда делает невозможным использование языка C плавным или значимым образом при выполнении связанных с аппаратными средствами программирования, таких как это.

Обход для этой ошибки языка C состоит в том, чтобы написать гигантский союз с двумя элементами, который представляет собой массив uint8_t, который является «суперструктурой», подобной описанной выше. На самом деле вы не будете использовать суперструктуру - вы, вероятно, не можете из-за заполнения, но, поместив ее в союз, вы вызываете специальное исключение из строкового сглаживания. Это значит, что больше не будет неопределенного поведения, и вы предотвратите агрессивные оптимизаторы компиляторов, такие как gcc, чтобы нарушить ваш код.

Еще одна особенность, связанная с gcc для этой ошибки языка C, заключается в компиляции с gcc -fno-strict-aliasing. Компиляторы встроенных систем обычно работают лучше, чем gcc для этого случая, поскольку они не соответствуют стандарту C, но вместо этого преобразования указателей ведут себя детерминистически нестандартным образом. Например, в таких компиляторах код, как (uint16_t*)my_uint8t, детерминистически обрабатывает данные с заостренными данными как uint16_t, вместо того, чтобы молча приводить к сбою и записи вашей программы.

-1

вместо массива думает автогенерированная структура, которая генерирует код, который вы компилируете дальше. Например ввод:

const union detail configuration[] = { 
    { .a = { .some_field = 23 } }, 
    { .a = { .some_field = 45 } }, 
    { .b = { .another_field = 12 } } 
}; 

может быть разобран для создания

#pragma pack(1) 
typedef struct { 
    struct a a1; 
    struct a a2; 
    struct b b1; 
} genStruct; 

genstruct glostruct = {{23}, {45}, {12} }; 

что-то подобное.

+2

Это не решает проблему, дополнение. – Lundin

+0

Извините, теперь используйте прагма-пакет на сгенерированной структуре, а также все структуры, используемые, чтобы уйти с заполнением. – Nish