Is sizeof (T) == sizeof (int)?

Я рассматривал проект стандарта и не могу найти то, что я ищу.

Если у меня есть тип стандартного макета

struct T {
   unsigned handle;
};

Тогда я знаю, что reinterpret_cast<unsigned*>(&t) == &t.handle для некоторого T t;

Цель состоит в том, чтобы создать несколько vector<T> v и передать &v[0] функции C, которая ожидает указатель на массив целых чисел без знака.

Итак, определяет ли стандарт sizeof(T) == sizeof(unsigned) и это означает, что массив T будет иметь тот же макет, что и массив unsigned?

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

Я прочитал несколько абзацев, которые, похоже, намекают, что, возможно, это может быть правдой, но ничего, что попадает в гвоздь на голове. Например:

§ 9.2.17

Два типа стандартной структуры (раздел 9) являются совместимыми с макетами, если они имеют одинаковое количество нестатических элементов данных и соответствующих нестатические элементы данных (в порядке объявления) имеют совместимость с макетами Типы

Это не совсем то, что я ищу, я не думаю.

Ответ 1

Вы, по сути, спрашиваете, учитывая:

struct T {
    U handle;
};

гарантировано ли это, что sizeof(T) == sizeof(U). Нет, это не так.

В разделе 9.2/17 стандарта ISO С++ 03 говорится:

Указатель на объект POD-структуры, соответствующим образом преобразованный с использованием reinterpret_cast, указывает на его начальный член (или если этот член является бит-поле, затем в блок, в котором он находится) и наоборот.

Предположим, что у вас есть массив struct T. Часть наоборот означает, что адрес любого из членов T::handle также должен быть действительным адресом a struct T. Предположим, что эти члены имеют тип char и что ваше утверждение верно. Это означало бы, что struct T будет иметь неглавный адрес, что кажется маловероятным. Стандарт обычно старается не привязывать руки реализаций таким образом. Для того, чтобы ваше утверждение было истинным, стандарт должен был бы требовать, чтобы struct T допускалось иметь несогласованные адреса. И это должно быть разрешено для всех структур, потому что struct T может быть непрозрачным, непрозрачным.

Далее, в разделе 9.2/17 говорится:

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

Что, по-другому, означает, что нет никакой гарантии, что никогда не будет заполнения.

Ответ 2

Я использую среду Borland и для них:

T - это структура в вашем случае, поэтому sizeof (T) - это размер структуры

  • который зависит от настроек #pragma pack и align вашего компилятора
  • поэтому иногда это может быть больше sizeof (без знака)!!!

по той же причине, если у вас есть 4Byte struct (uint32) и 16Byte allign

  • struct T {uint32 u; };
  • тогда T a [100] не совпадает с uint32 a [100];
  • потому что T is uint32 + 12 Байт пустое пространство!!!

Ответ 3

RETRACTION: аргумент ошибочен. Доказательство леммы 2 полагается на скрытую предпосылку, что выравнивание агрегатного типа определяется строго выравниваниями его типов членов. Как Dyp указывает в комментарии, эта предпосылка не поддерживается стандартом. Поэтому для struct { Foo f } допустимо иметь более строгие требования к выравниванию, чтобы Foo.


Здесь я буду играть адвоката дьявола, поскольку никто больше не хочет этого. Я буду утверждать, что стандартный С++ (я буду ссылаться на N3797 здесь) гарантирует, что sizeof(T) == sizeof(U), когда T является стандартным классом макета ( 9/7) с выравниванием по умолчанию, имеющим один нестатический элемент данных с выравниванием по умолчанию U, например
struct T { // or class, or union
  U u;
};

Хорошо известно, что:

  • sizeof(T) >= sizeof(U)
  • offsetof(T, u) == 0 (9.2/19)
  • U должен быть стандартным типом макета для T для стандартного класса макета
  • U имеет представление, состоящее из точно sizeof(U) смежных байтов памяти (1.8/5)

Вместе эти факты подразумевают, что первые sizeof(U) байты представления T заняты представлением U. Если sizeof(T) > sizeof(U), избыточные байты должны быть затем заполнены хвостом: неиспользуемые байты заполнения, вставленные после представления U в T.

Аргумент короче:

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

Потенциальные источники заполнения

В каких обстоятельствах стандарт позволяет реализации добавлять такое дополнение к представлению стандартного класса макета? Когда это необходимо для выравнивания: по 3.11/1, "выравнивание представляет собой целочисленное значение, определенное реализацией, представляющее количество байтов между последовательными адресами, в которых может быть выделен данный объект". Есть два упоминания о добавлении дополнения, как для выравнивания:

  • 5.3.3 Sizeof [expr.sizeof]/2 states "При применении к эталонному или ссылочному типу результат - это размер ссылочного типа. к классу, результатом является количество байтов в объекте этого класса, включая любое дополнение, необходимое для размещения объектов этого типа в массиве. Размер самого производного класса должен быть больше нуля (1.8). Результатом применения sizeof к подобъекту базового класса является размер типа базового класса. 77 При применении к массиву результатом является общее количество байтов в массиве. Это означает, что размер массива из n элементов в n раз превышает размер элемента. "

  • 9.2 Члены класса [class.mem]/13 states "Требования к выравниванию реализации могут привести к тому, что сразу два соседних элемента не будут распределены сразу после друг друга, поэтому могут потребоваться требования для пространства для управления виртуальными функциями (10.3) и виртуальной базой классы (10.1)."

  • (Примечательно, что в стандарте С++ нет содержащего описания, позволяющего реализациям вставлять дополнения в структуры, как в стандартах C, например, N1570 (C11-ish) §6.7.2.1/15 "Возможно, внутри объекта структуры, но не в его начале". и /17 "В конце структуры или объединения может быть неназванное дополнение".)

Ясно, что текст 9.2 не относится к нашей проблеме, так как (a) T имеет только один элемент и, следовательно, не имеет "смежных элементов" и (b) T является стандартным макетом и, следовательно, не имеет виртуального функций или виртуальных базовых классов (за 9/7). Демонстрация того, что 5.3.3/2 не позволяет заполнить нашу проблему, является более сложной задачей.


Некоторые предварительные условия

Лемма 1: Для любого типа W с выравниванием по умолчанию alignof(W) делит sizeof(W): В 5.3.3/2 размер массива из n элементов типа W ровно n раз sizeof(W) (т.е. не существует "внешнего" дополнения между элементами массива). Адреса последовательных элементов массива затем выделяются sizeof(W) байтами. По определению выравнивания, тогда должно быть, что alignof(W) делит sizeof(W).

Лемма 2: Выравнивание alignof(W) стандартного класса компоновки по умолчанию W с только членами с выравниванием по умолчанию является наименее общим кратным LCM(W) выравниваний элементов данных (или 1 если их нет):Учитывая адрес, по которому может быть выделен объект W, адрес LCM(W) bytes away также должен быть соответствующим образом выровнен: разница между адресами субобъектов-членов также будет LCM(W) байт, а выравнивание каждого такого член подобъекта делит LCM(W). По определению выравнивания в 3.11/1 мы имеем, что alignof(W) делит LCM(W). Любое целое число байтов n < LCM(W) не должно делиться на выравнивание некоторого члена v из W, поэтому адрес, который только n байтов от адреса, на котором может быть объект W, может быть следовательно, не соответствующим образом выровнены для объекта W, т.е. alignof(W) >= LCM(W). Учитывая, что alignof(W) делит LCM(W) и alignof(W) >= LCM(W), мы имеем alignof(W) == LCM(W).


Заключение

Применение этой леммы к исходной задаче имеет непосредственное следствие, что alignof(T) == alignof(U). Итак, сколько отступов может быть "необходимо для размещения объектов этого типа в массиве"? Никто. Поскольку alignof(T) == alignof(U) по второй лемме, а alignof(U) делит sizeof(U) на первый, должно быть, что alignof(T) делит sizeof(U), так что для размещения объектов типа T в массиве нулевые байты заполнения требуется в массиве.

Поскольку все возможные источники байтов заполнения удалены, реализация может не добавлять дополнение к T, и мы должны иметь sizeof(T) == sizeof(U) по мере необходимости.