Структура нулевого размера

#c

#c

Вопрос:

В соответствии со стандартом C (унаследованным от C) пустые структуры, тем не менее, имеют ненулевой размер. Причина этого (жалкое ИМХО) заключается в том, что две разные переменные должны иметь разные адреса. Теперь наследование пустой структуры не всегда «раздувает» объект. Но в некоторых ситуациях это так.

У меня довольно сложная архитектура классов, включающая жестокое шаблонное вуду. В результате конечные классы (экземпляры которых мне нужно создать) могут иметь несколько унаследованных пустых структур. Из-за этого часть из них может быть в конечном итоге раздута. И хуже всего то, что их расположение памяти фактически зависит от порядка наследования.

Я бы хотел избавиться от всего этого, если это возможно.

Существует ли компилятор C , который можно настроить для устранения этой потери пространства за счет фактического нарушения стандарта?

Редактировать:

Я имею в виду это:

 struct Empty1 {};
struct Empty2 {};

struct NonEmpty {
    int Value;
};

struct MyClass1
    :public NonEmpty
    ,public Empty1
    ,public Empty2
{
};

struct MyClass2
    :public Empty1
    ,public NonEmpty
    ,public Empty2
{
};

struct MyClass3
    :public Empty1
    ,public Empty2
    ,public NonEmpty
{
};

STATIC_ASSERT(sizeof(MyClass1) == 8);
STATIC_ASSERT(sizeof(MyClass2) == 4);
STATIC_ASSERT(sizeof(MyClass3) == 8);
  

Не только пустые структуры раздувают объект (когда наследуется более одной такой вещи), но и результат зависит от порядка наследования пустых структур.

Комментарии:

1. Оптимизация пустой базы явно разрешена стандартом языка. «Их расположение памяти на самом деле зависит от порядка наследования» вызывает некоторое беспокойство, поскольку нет никакой гарантии относительно расположения подобъектов базового класса. У вас есть пример того, чего вы пытаетесь достичь?

2. Вы действительно продемонстрировали, что это проблема с вашим текущим компилятором? Я ожидал бы, что пустой класс, используемый в качестве базового класса, не займет места в производном классе; конкретный пример был бы полезен.

3. Причина на самом деле не жалкая. Вы просто на самом деле не ссылаетесь на это; настоящая причина гораздо сложнее. Стандарт, возможно , обошелся без этого ограничения (я не уверен), но для этого потребовалось бы сложное доказательство того, что это всегда приводит к хорошо сформированным программам.

4. Изменение порядка наследования не является незначительным изменением. Если ваш тип не POD, ваш код должен быть написан таким образом, чтобы не имело значения, в каком порядке именуются базовые классы или выполняется ли EBO. Если вас беспокоит расположение памяти, вы должны использовать типы POD. По сути, нет никаких гарантий для типов, отличных от POD.

5. Держу пари, вы компилируете под MSVC? Это известная проблема с множественным наследованием: connect.microsoft.com/VisualStudio/feedback/details/100686/… Решения включают в себя не беспокоиться об этом, переключиться на «лучший» компилятор и избежать множественного наследования.

Ответ №1:

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

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

Ответ №2:

В большинстве современных компиляторов c вы обнаружите, что:

 struct a { }; // empty struct
struct b : a { int x}; // inherits from empty struct.

assert(sizeof(b)==sizeof(int)); // despite sizeof(a) >0
  

Это практически снимает ваши опасения?

Комментарии:

1. 1 только потому, что valdo мог бы легко написать несколько тестов, чтобы убедиться, что оптимизация выполняется.

2. @Chris: Это на самом деле требуется стандартными правилами компоновки C 11 . Проблема возникает только тогда, когда у вас есть несколько объектов с NSDM.

Ответ №3:

Существует ли компилятор C , который можно настроить для устранения этой потери пространства, …

Да. gcc-4.3.4

… за счет нарушения стандарта на самом деле?

Нет, стандарт допускает желаемое поведение.

Комментарии:

1. Большое спасибо, это отвечает на мой вопрос. Существует ли специальная конфигурация, необходимая для включения этого? Возможно ли также устранить ненужное пространство для пустых объектов? То есть объекты, состоящие только из пустых баз.

2. @valdo: Нет, вы не могли бы поместить их в массивы (поскольку элементы массива находятся по адресам, которые отличаются на sizeof(T) ). Поскольку вы не можете поместить подобъект базового класса сам по себе в массив, он может иметь нулевое пространство.

3. @MSalters: Я думал о классе, который имеет переменные-члены (объекты) различных видов. Некоторые из них могут в конечном итоге получить пустые структуры.

4. @valdo: переменные-члены — это еще один случай, и они не подпадают под исключение базового класса. Смотрите boost::compressed_pair класс, который творчески использует исключение базового класса

Ответ №4:

g поддерживает массивы нулевого размера, вы можете использовать один из:

 struct Empty1 {
  int dummy[0];
};
  

или

 struct Empty2 {
  int dummy[];
};
  

Он генерирует предупреждение, только если используются флаги ‘-pedantic’.

Комментарии:

1. Есть ли какие-либо сведения об этом драгоценном камне в стандарте или где-нибудь еще? Насколько она переносима?

Ответ №5:

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

Чтобы «решить» эту проблему, вам необходимо убедиться, что пустые классы или структуры, от которых вы наследуете другой класс, не имеют общих предков.

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