Поддержание массива указателей в C, который указывает на два связанных типа

У меня есть структура, как показано ниже

struct things { BOOL_T is_copy; /* is false */ handle1 h1; handle2 h2; int n; void * memory; }; 

Иногда я делаю копию объектов things в ниже структуры

 struct copy_of_things { BOOL_T is_copy; /* is true */ handle1 h1; /* I don't need h2 and n here */ void * memory; /* copied from things object and things object allocates new memory */ int another_member; }; 

Также у меня есть массив структуры в менеджере, который сохраняет все things и структуру copy_of_things живущих в моей программе (назовите это struct things *things_array[SIZE_OF_ARRAY]; ). Я не могу управлять 2 массивами из-за требований к дизайну (массивы похожи на хеш). Чтобы включить это, я сделал тип этого массива как thing * и изменил тип copy_of_things как copy_of_things ниже

 struct copy_of_things { struct things ths; int another_member; }; 

Теперь я могу прочитать член is_copy из моих элементов массива и решить, следует ли его интерпретировать как things или copy_of_things .

Я чувствую, что это не только inefficient с точки зрения памяти, но и ugly .

Решение 2 Я также планирую использовать тип массива struct of type(is_copy) and a union .

 struct things { BOOL_T is_copy; union { struct { /* is_copy = false */ handle1 h1; handle2 h2; int n; void * memory; } t; struct { /* is_copy = true */ handle1 h1; void * memory; int another_member; } c; }; 

Но при просмотре я нашел этот дизайн также уродливым.

Решение 3 Я планирую сохранить BOOL_T is_copy; как первый элемент структуры и сохранить массив типа BOOL_T . Прочитав содержимое BOOL_T я могу BOOL_T ссылку на мой указатель на вещи или copy_of_things. Я не уверен, что это хорошее решение и обеспечивает четкое поведение (при более высоком уровне оптимизации), поскольку один и тот же адрес интерпретируется как разные типы.

Вопрос. Есть ли лучшее решение для моей проблемы, которое переносится на любой платформе.

РЕДАКТИРОВАТЬ
Спасибо за ответы. Таким образом, есть две предложенные альтернативы.

  1. Использовать союзы. Недостатком подхода является то, что для копирования требуется больше памяти. В моем случае sizeof copy_of_things значительно меньше размера sizeof things . Одним из способов было бы выделение достаточно байтов, в которых может находиться фактический объект.
  2. Используйте общую структуру и сделайте ее первым членом как copy_of_things и things . Здесь я бы закончил удаление ссылки на то же место памяти с двумя типами (struct common и struct things или struct copy_of_things). Я не уверен, что строгое правило сглаживания не укусит меня.
  3. Еще одно решение может содержать первый член обеих структур как char is_copy; /* \0 if not a copy, non zero otherwise char is_copy; /* \0 if not a copy, non zero otherwise и доступ к указателю только как char * или things * или copy_of_things * .

Еще открытый вопрос
Я видел решение 2, используемое во многих местах. Правильно ли это правило сглаживания ? Есть ли лучшее решение моих проблем, поскольку код будет скомпилирован для разных компиляторов. Размер массива обратного отображения большой, поэтому я избегаю использовать объединение или решение, которое увеличивает размер обратного отображения. Количество вещей (и копия) меньше, поэтому можно добавить туда новый элемент данных.

Вы можете поделиться некоторыми членами этих структур с более компактным объединением:

 struct things { BOOL_T is_copy; handle1 h1; void * memory; union { struct { /* is_copy = false */ handle2 h2; int n; } t; struct { /* is_copy = true */ int another_member; } c; }; 

Наличие структуры внутри других структур является обычным способом эмуляции наследования в C. Важно то, что общая структура должна содержать минимальный dataset, общий для всех структур в иерархии «наследования», и что он всегда должен быть первый член в наследующих структурах.

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

Другая важная вещь, касающаяся размещения структуры в первую очередь, заключается в том, что тогда смещения общих членов будут одинаковыми, и вы можете легко обработать указатель на большую структуру как указатель на меньшую базовую структуру.


Например, в вашей is_copy член is_copy будет иметь различное смещение в зависимости от того, к какой структуре у вас есть указатель, поэтому вам нужно знать, на какую структуру указывает указатель, прежде чем вы сможете получить доступ к is_copy , какие поражения вашей цели.

Другой способ, с помощью которого вы устанавливаете базовую структуру в качестве члена внутри расширенной структуры, – это то, о чем я говорю выше.


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


Что касается переносимости, если вы не переносите структуры между платформами или используете разные компиляторы для разных частей вашего приложения, то версия union является наиболее переносимой исходным кодом. Схема «наследования» будет работать на всех современных ПК-подобных системах и их компиляторах и проделать это в течение длительного времени, но нет никакой гарантии, что она будет работать на всех системах и всех компиляторах (если вы планируете портировать кода в редкую систему со странным оборудованием и компилятором, которые вы, возможно, захотите посмотреть, но случаи, когда схема «наследования» не будет работать, очень мала, и большинство людей никогда не соприкасаются с такой системой за всю свою жизнь. )

Я не знаком со многими из этих слов здесь, но, пожалуйста, рассмотрите следующее мое предложение:

 struct things { BOOL_T is_copy; union { struct { handle1 h1; handle2 h2; int n; void * memory; } * t; struct { handle1 h1; void * memory; int another_member; } * c; }; }; 

Выглядит ужасно похоже на тот, что в вашем вопросе, хотя и с одним ключевым отличием. Обратите внимание на звездочки за именами переменных, делая их указателем на структуры.

Таким образом, t и c будут иметь одинаковый размер, поэтому при использовании одной из ветвей объединения не будет посторонней памяти. Это немного похоже на ответ sclarke81 , но здесь он более отчетливый, также вам не нужно будет выводить void * object на структуру, на которую вы выделили память для доступа к своим членам. Т.е. вы можете просто написать следующее:

 if (mything.is_copy) { mything.c->another_member; } else { mything.t->n; } 

Вместо:

 if (mything.obj_type == copy) { ((struct copy_of_things) mything.object)->another_member; } else { ((struct things) mything.object)->n; } 

Я понятия не имею, покрывает ли это ваши потребности, просто подсказка.

Как насчет чего-то вроде этого:

 enum object_types { thing, copy }; struct thing_array_item { void *object; enum object_types obj_type; }; struct thing_array_item *things_array[ SIZE_OF_ARRAY ]; 

Это означает, что у thing_array_item есть указатель на ваш объект, а перечисление указывает вам, как его интерпретировать. Вы можете просто malloc и освободить память для объектов по своему усмотрению.

Альтернативой было бы покончить с перечислением:

 struct thing_array_item { struct things *obj1; struct copy_of_things *obj2; }; 

С этим вы просто установите указатель, который вы не используете в NULL . Тогда ненулевой указатель в любом thing_array_item является указателем, который вы используете.

У меня появилось много хороших идей для моей проблемы. Но мне кажется, что я не смог эффективно записать все детали моего вопроса.

Вот окончательное решение, которое я придумал:

[1] Напишите общую структуру, содержащую элементы, к которым обращаются через обратное сопоставление.

 struct common { handle1 h1; void * memory; }; 

[2] Теперь сохраните это как член в обеих структурах.

 struct things { handle2 h2; int n; struct common cmn; }; struct copy_of_things { BOOL_T is_copy; /* is true */ int another_member; struct common cmn; }; 

[3] Изменить тип обратного сопоставления на struct common *

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