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)
по мере необходимости.