TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.37K subscribers
Post #1028 3.97K
Выравнивание. Принципы
#новичкам

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

🔍 Однобайтовые данные не нужно выравнивать. Да, процессор читает данные определенными порциями, которые больше 1 байта. Но для любой взятой char переменной, при попытке ее прочитать ее значение будет лежат в самом начале порции. Минимальная адресуемая единица - это 1 байт, поэтому однобайтовые данные неделимы. Не нужно никаких дополнительных плясок.

Для наглядности можно представить себе массив чаров:

char array[10];
std::cout << sizeof(array) << std::endl;
// OUTPUT: 10


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

🔍 Адрес поля тривиального типа должен быть выровнен по размеру этого типа

- для short адреса должны быть кратными 2
- для int адреса должны быть кратными 4
- для size_t адреса должны быть кратными 8
- для float адреса должны быть кратными 4
- для double адреса должны быть кратными 8
- для указателей адреса должны быть кратными 8

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

Все это актуально для современных 64-битных десктопов.

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

Например:

struct A {
char a;
char b;
};

struct B {
char first;
A second;
};

std::cout << sizeof(A) << std::endl;
std::cout << sizeof(B) << std::endl;
// OUTPUT:
// 2
// 3


У нас есть составная структура А, размер которой равен 2 байтам. Структура B содержит А в качестве поля. Так как в А только char поля, для доступа к ним не нужны паддинги. Поэтому размер В равен 3-м.

Но если будет вот так:

struct A {
char a;
double b;
};

struct B {
char first;
A second;
};

std::cout << sizeof(A) << std::endl;
std::cout << sizeof(B) << std::endl;
// OUTPUT:
// 16
// 24


Мало того, что паддинг размером 7 добавися перед полем b структуры А, так он же еще добавится перед `second` в В. И в B он такого размера именно потому, чтобы доступ до b в итоге всех размещений был выровнен.

🔍 Паддинг добавляется в основном перед тем полем, адрес которого должен быть выровнен. Мы это уже увидели на практике.

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

Допустим, у нас есть структура и мы хотим из нее сделать массив:

struct A {
double a;
char b;
};

A array[10];


Паддинг в 7 байт нужно добавить для того, чтобы адреса поля a из всех элементов, начиная со второго, тоже были выровнены по границе 8.

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

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

struct A {
char a;
// 7 byte padding
double d;
short b;
// 2 byte padding
int c;
};

struct B {
double d;
int c;
short b;
char a;
// 1 byte padding
};

std::cout << sizeof(A) << std::endl;
std::cout << sizeof(B) << std::endl;
// OUTPUT:
// 24
// 16


Реальный размер данных - 15 байт. Для double поля в любом случае нужно выравнивание по 8-ке. Но правильно расположив данные, мы экономим 8 байт.

Тема выравнивания довольно большая и мы прошлись только по верхам. Но даже эти простые принципы помогут вам правильно оценивать размер структур и организовывать их поля оптимальным образом.

Align yourself. Stay cool.

#cppcore #optimization #compiler
  • 🔥 27
  • ❤ 16
  • 👍 12
More from @grokaemcpp
  1. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  2. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  3. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  4. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
  5. Sep 28, 2026​​Stacktrace #опытным Одна из проблема исключений - непонятно, откуда оно прилетело. Ну да…
  6. Sep 25, 2026​​std::spanstream #опытным Радостная весть для всех, кто пользуется iostreams! В C++23 доб…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →