Рассмотрим следующую простую структуру:
struct A
{
float data[16];
};
Мой вопрос:
Предполагая, что платформа с float
представляет собой 32-битное число с плавающей точкой IEEE754 (если это вообще имеет значение), гарантирует ли стандарт C++ ожидаемый макет памяти для struct A
? Если нет, что это гарантирует и/или как обеспечить соблюдение гарантий?
Под ожидаемой компоновкой памяти я подразумеваю, что структура занимает 16*4=64
байта в памяти, каждый из 4
последовательных байтов занят одним float
из массива data
. Другими словами, ожидаемое расположение памяти означает следующие прохождения теста:
static_assert(sizeof(A) == 16 * sizeof(float));
static_assert(offsetof(A, data[0]) == 0 * sizeof(float));
static_assert(offsetof(A, data[1]) == 1 * sizeof(float));
...
static_assert(offsetof(A, data[15]) == 15 * sizeof(float));
(offsetof
здесь допустим, поскольку A
- стандартное расположение, см. ниже)
В случае, если это вас беспокоит, тест фактически проходит на wandbox с gcc 9 HEAD. Я никогда не встречал комбинацию платформы и компилятора, которая бы свидетельствовала о том, что этот тест может провалиться, и я хотел бы узнать о них в случае их существования.
Зачем кому-то все равно:
- Подобные SSE оптимизации требуют определенной компоновки памяти (и выравнивания, которое я игнорирую в этом вопросе, поскольку с ним можно справиться, используя стандартный
alignas
). - Сериализация такой структуры будет просто сводиться к красивому и переносимому
write_bytes(&x, sizeof(A))
. - Некоторые API (например, OpenGL, в частности, скажем, glUniformMatrix4fv) ожидают такого точного расположения памяти. Конечно, можно просто передать указатель на массив
data
чтобы передать один объект этого типа, но для последовательности из них (скажем, для загрузки атрибутов вершин матричного типа) определенная схема памяти все еще необходима.
Что на самом деле гарантировано:
Это те вещи, которые, насколько мне известно, можно ожидать от struct A
:
- Это стандартная раскладка
- Как следствие стандартного размещения, указатель на
A
может бытьreinterpret_cast
для указателя на его первый элемент данных (который, по-видимому,data[0]
?), То есть нет заполнения перед первым элементом.
Две оставшиеся гарантии, которые не предоставлены (насколько мне известно) стандартом:
- Между элементами массива примитивного типа нет заполнения (я уверен, что это неверно, но мне не удалось найти подтверждающую ссылку),
- Заполнение после массива
data
внутриstruct A
.