Если вы собираетесь портировать только на машины (компиляторы), которые поддерживают C99, то вы можете использовать «члены гибкого массива», которые являются переносимой версией «struct hack».
§6.7.2.1 Структура и объединение спецификаторов
¶16 В особом случае последний элемент структуры с более чем одним именованным элементом может
иметь неполный тип массива; это называется гибкий член массива . С двумя
исключения, гибкий член массива игнорируется. Во-первых, размер структуры должен быть
равно смещению последнего элемента в остальном идентичной структуры, которая заменяет
элемент гибкого массива с массивом неопределенной длины. 106) Секунда, когда .
(или ->
)
Оператор имеет левый операнд, который является (указатель) на структуру с членом гибкого массива
и правильный операнд называет этот член, он ведет себя так, как если бы этот член был заменен
с самым длинным массивом (с тем же типом элемента), который не сделал бы структуру
больше, чем объект, к которому осуществляется доступ; смещение массива должно оставаться смещение
гибкий элемент массива, даже если это будет отличаться от элемента массива замены. Если это
массив не имеет элементов, он ведет себя так, как если бы он имел один элемент, но поведение
не определено, если делается какая-либо попытка получить доступ к этому элементу или создать указатель один раз
это.
EX17 ПРИМЕР Предполагая, что все элементы массива выровнены одинаково после объявлений:
struct s { int n; double d[]; };
struct ss { int n; double d[1]; };
Три выражения:
sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)
имеют одинаковое значение. Структура struct s
имеет гибкий элемент массива d
.
106) Длина не указана, чтобы учесть тот факт, что реализации могут давать разные элементы массива
выравнивания в соответствии с их длиной.
В контексте это означает, что вы можете написать:
typedef struct mystruct
{
int x;
struct y *list[];
} mystruct; // Note that this is necessary; C++ automatically recognizes mystruct; C does not.
Чтобы выделить место, вы можете использовать:
mystruct *h1 = (mystruct *)malloc(sizeof(mystruct) + 10 * sizeof(struct y *));
Это выделяет mystruct
с достаточным пространством для 10 указателей в массиве. Позже вы можете изменить размер памяти с помощью:
mystruct *new_h1 = (mystruct *)realloc(h1, sizeof(mystruct) + 20 * sizeof(struct y *));
if (new_h1 == 0)
...handle out of memory error, but note that h1 is still valid...
h1 = new_h1; // Safe
(Обратите внимание, что я тщательно не назначаю h1
при перераспределении; это приведет к утечке памяти в случае сбоя выделения памяти.)
Вы можете ссылаться на них, как если бы массив был там:
h1->list[0] = ...;
Обратите внимание, что вы не можете иметь массив mystruct
, но вы можете иметь массив указателей на mystruct
. Вы также должны следить за размером массива; вы обычно делаете что-то вроде того, чтобы один из фиксированных членов записал выделенный размер.