#опытным
Представим, что мы проектируем дизайн квартиры. Для того, чтобы расставлять предметы, нужно знать их размеры. Но не только это. Есть ещё одна важная характеристика — требования к размещению. Некоторые предметы можно ставить где угодно, другие требуют специального места у стены, третьи должны стоять строго в углу, четвертые прекрасно разместятся у вас в помойке за ненадобностью.
Чтобы дизайн получился хороший и все купленные предметы поместились и органично стояли на свои местах, нужно все эти требования знать.
С мебелью все просто - идешь и меряешь. Или смотришь размеры в онлайн магазине.
С данными так же, для них есть определенные требования к размеру и размещению. Но как их узнать?
Ну размер типа узнать просто - используем оператор sizeof, это все знают.
А требования к выравниваю как узнать?
Для этого есть C++11 оператор alignof. Он возвращает требуемое выравнивание для типа в байтах. Это значит, что адрес начала объекта данного типа был кратен результату alignof. Выравнивание должно быть степенью двойки(компьютеры у нас все-таки двоичные и все вокруг ее степени крутится).
В прошлом посте я голосновно перечислил выравнивания для базовых типов. Но теперь есть пруфы:
std::println("char требует выравнивания: {} байт", alignof(char));
std::println("short требует выравнивания: {} байт", alignof(short));
std::println("int требует выравнивания: {} байт", alignof(int));
std::println("size_t требует выравнивания: {} байт", alignof(size_t));
std::println("float требует выравнивания: {} байт", alignof(float));
std::println("double требует выравнивания: {} байт", alignof(double));
std::println("void* требует выравнивания: {} байт", alignof(void*));
// OUTPUT:
// char требует выравнивания: 1 байт
// short требует выравнивания: 2 байт
// int требует выравнивания: 4 байт
// size_t требует выравнивания: 8 байт
// float требует выравнивания: 4 байт
// double требует выравнивания: 8 байт
// void* требует выравнивания: 8 байтОчень важно еще раз проговорить: размер типа и его выравнивание - это разные вещи. Так уж получилось, что они совпадают у тривиальных типов, но это не так для кастомных. Вот например:
std::println("std::string: размер={}, выравнивание={}", sizeof(std::string), alignof(std::string));
std::println("std::vector<int>: размер={}, выравнивание={}", sizeof(std::vector<int>), alignof(std::vector<int>));
// OUTPUT:
// std::string: размер=32, выравнивание=8
// std::vector<int>: размер=24, выравнивание=8Кстати говоря. 8 - не максимальный размер выравнивания, некоторые типы требуют большего числа:
std::println("alignof(long double) = {}", alignof(long double));
std::println("alignof(std::max_align_t) = {}", alignof(std::max_align_t));
std::println("alignof(__m128) = {}", alignof(__m128));
// OUTPUT
// alignof(long double) = 16
// alignof(max_align_t) = 16
// alignof(__m128) = 16double на самом деле не самую большую точность имеет из стандартных типов. Есть тип long double, его размер и выравнивание равны 16(на gcc и clang).
Также есть специальный тип std::max_align_t, чьи требования к выравниванию удовлетворяют любому скалярному типу. Тоже 16.
Для sse векторных регистров выравнивание тоже побольше.
Для чего этот оператор может применяться помимо учебных целей - рассмотрим в следующих постах.
Align yourself. Stay cool.
#cppcore #cpp11 #compiler