Здравствуйте! На этой лекции мы продолжим говорить о выравнивании данных в памяти. На прошлой лекции мы говорили о выравнивании элементарных типов, на этой лекции мы говорим о выравнивании более сложных типов данных. Посмотрим на наш код, в котором мы написали две структуры данных. Первая внутренняя структура Inner, она состоит из двух простых типов Int и Char, размеры которых равны 4.1, соответственно, и структура Outer внешняя, которая более сложная, которая в своих полях хранит два экземпляра класса Inner, того класса, который мы только что написали. И, заметьте, что про размер типа Inner мы пока ничего не знаем. Мы можем сделать некоторые предположения, например, я бы предположил, что размер Inner равен восемь из-за выравнивания. Мы можем это проверить вызвав Sizeof. Мы здесь написали для Sizeof более удобный macros, чтобы вывод был красивее. Давайте, запустим, посмотрим, что получается. Программа нам сообщает, что Sizeof типа Inner действительно равен восемь байтов. Мы можем тут написать восемь. И теперь мы хотим сделать предположение о размере типа Outer, чему же он равен. Хороший вопрос, мы на него не можем точно ответить сейчас потому, что мы ничего не знаем о выравнивании типа Inner, мы знаем, что поля элементарных типов выравниваются по своему размеру, но Inner не элементарный тип. Чему же равно выравнивание типа Inner? По какой границе он выровнен? Для того, чтобы это узнать, мы можем воспользоваться оператором Аlignof. Есть такой оператор в С++ помимо Sizeof. Давайте расширим тот macros, который у нас написан для печати отладочной информации. Мы узнали, что Аlignof типа Inner номер равен четыре байта. Это значит, что все объекты типа Inner будут выравнены по границе четыре байта и мы можем даже сделать предположение, почему так происходит, потому что Inner содержит в себе поле типа Int, размер которого равен четыре байта. Теперь мы можем сделать некое предположение, чему же равен размер типа Outer. Если вы предполагали, что размер типа Outer равен 20 байтов, то вы совершенно правы и Аlignof типа Outer равен четыре байта, то есть по границе четыре байта будут выровнены все объекты этого типа. Дальшe. Что мы хотим сделать? Мы хотим посмотреть подробно на схему, как устроены в памяти объекты типа Outer, для этого вызовем уже известный нам macros OffSetOf, в котором посмотрим на смещение всех полей. Вот они эти смещения 08 и 16, мы сейчас можем их нарисовать на схеме и посмотреть подробно на рисунке, что же у нас там получается. Сначала разберемся с типом Inner, почему у него размер равен восьми. Да, на прошлой лекции мы узнали о выравнивании данных, но тут же Int стоит на первом месте, то есть он и так выровнен. Размер Сhar это один байт, так что его и выравнивать не нужно. Зачем нужны лишние три байта на пэддинг в конце структуры после Char? Это можно понять представив себе массив из объектов типа Inner, массив хранит данные в памяти подряд, то есть объекты располагаются один за одним. Посмотрим на слайд. Если бы не было пэддинга в конце структуры, то поля Int следующих объектов в массиве оказались бы не выровненными и процессору было бы сложнее к ним обращаться. Теперь перейдем к более сложному вопросу, к структуре Outer. Изобразим схему расположения в памяти ее двадцати байтов. Что мы видим? Поля сложных типов Inner1 и Inner2 тоже выровнены внутри нее. Пэддинг привычно отмечен серым цветом, в самый конец структуры после CharС добавлен пэддинг по причинам, которые были рассмотрены на предыдущем слайде. Теоретически это все может выглядеть убедительно, но как проверить, что наша теория верна, что вообще компилятор тоже в курсе, что такое пэддинг и размещает его так, как мы предполагаем. Для подтверждения сказанного, давайте, вернемся в VDE и попросим подтверждений у компилятора. Что мы можем сделать в VDE? Мы можем передать флаг, опцию компилятора, который включает дополнительные ворнинги, ворнинги о пэддинге, вот это слово "padded". Когда этот флаг включен, у нас компилятор будет сообщать в виде ворнингов о всех пэддингах, которые он добавляет в структуру. Ну, что ж, соберем код и посмотрим на ворнинги. У нас есть два ворнинга о типах Inner и Outer. Первый ворнинг говорит, что у нас добавлен пэддинг в тип Inner размера три байта из-за выравнивания. Это ровно то, что мы видели на схеме. Второй ворнинг говорит, что в тип Outer также добавлен пэддинг размера три байта в конец. Да, это то, что мы предполагали. Все верно и компилятор нам это подтверждает. Итого, на этом занятии мы узнали, что поля сложных типов в классах тоже выравниваются, также не стоит забывать, что из-за выравнивания память может тратиться на пэддинг даже в конце объектов. А еще мы помимо Sizeof пользовались оператором Alignof, который возвращает, по какой границе будет объект в памяти.