Здравствуйте. Меня зовут Николай Субоч. Мы приступаем к новой теме черного пояса по C++. Мы рассмотрим, как в памяти хранятся объекты. Для начала вспомним структуру, хранящую автомобильный номер, которая вам знакома из коричневого пояса. Давайте вспомним, что это за структура. Она хранит пять полей типов "char" и "int". Три типа "char" и два типа "int". Что мы хотим с вами сделать? Мы хотим узнать, какой размер она будет иметь с помощью оператора "sizeof". Но прежде чем запустить эту программу, давайте попробуем сделать предположение. Вы уже знаете из желтого пояса, что тип "char" занимает в памяти один байт, а тип "int" занимает в памяти четыре байта. Поэтому, сложив все размеры, они равны в сумме 11, вы можете предположить, что "sizeof" в структуре "Plate" будет равен 11 байтов. Давайте запустим и проверим. Однако на деле оператор "sizeof" говорит нам, что размер будет равен 16 байтов. Нас это удивляет. Мы хотели бы разобраться. И чтобы разобраться, давайте попробуем вообще сделать какой-нибудь эксперимент, переставим два поля местами. Можно предположить, что от перестановки слагаемых сумма не меняется, однако если мы запустим этот код, мы получим "sizeof" равный 12. Это удивительно, потому что 12 не равно 11, которые мы ожидаем наивной суммой, и 12 не равно 16, как было в прошлый раз. Что же происходит? Для того, чтобы разобраться, что происходит, мы можем воспользоваться макросом "offsetof". Что делает макрос "offsetof"? Он для какой-нибудь структуры, для структуры "Plate", возвращает смещение поля в байтах от начала объекта. То есть если мы перечислим в этих пяти вызовах все поля структуры "Plate" и запустим программу, мы увидим, по каким смещениям в структуре лежат наши поля. Вот они: 0, 4, 5, 6, 8. И теперь мы можем нарисовать схему, картинку и посмотреть на схеме, как устроены данные в памяти. Перейдем к слайдам. На слайде числами отмечены смещение для всех полей, которые мы получили используя макрос "offsetof". Так как мы знаем, что размер всей структуры равен 12 байтов, мы можем нарисовать вот такую схему хранения объекта типа "Plate" в памяти. Здесь зеленым цветом отмечены поля типа "int", желтым поля типа "char" и один загадочный неиспользуемый байт серого цвета. Повторим такую же операцию для первой структуры, где первые поля "int" и "char" шли в другом порядке и размер которой равнялся 16 байтов. Возьмем смещение всех полей и нарисуем в соответствии с ним схему хранения объектов памяти. Здесь неиспользуемых байтов, которые отмечены серым цветом, будет заметно больше. Всего пять байтов. Нам нужно разобраться, откуда берутся лишние, неиспользуемые байты. Для начала посмотрим на схему, поищем на ней закономерности и сделаем какие-нибудь предположения. Мы можем заметить, что все поля типа "int" расположены не абы как, а аккуратно лежат внутри блоков по четыре байта, никогда не пересекая их границы. А для того, чтобы поля типа "int" лежали полностью в своих блоках, компилятор подкладывает между ними неиспользуемые служебные байты, которые на схеме обозначены серым цветом. У таких байтов даже есть специальные названия, они называются "padding". Запомните это слово, вы еще неоднократно встретите его в следующих лекциях. Собственно поэтому от порядка полей в структуре может изменяться ее размер. В том варианте структуры, когда размер равнялся 12 байтов, понадобился лишь один байт для padding. Итого получилось 11 байтов полезной информации плюс один байт на padding. И в сумме "sizeof" равен 12 байтов. Логично задаться вопросом: а зачем, собственно, вообще нужно выравнивание данных и почему поля типа "int" выравниваются по границе четыре байта? Так вот, выравнивание необходимо для эффективного обращения процессора к данным в памяти. Процессору проще обращаться к данным, когда они аккуратно лежат по блокам своего размера. Здесь можно провести аналогию с квартирами в жилом доме. Представьте, что у вас в доме не одна квартира целиком, а две полу-квартиры и вам приходится из одной комнаты в другую ходить через коридор. Точно так же и процессору удобнее обращаться к памяти, когда данные в памяти лежат аккуратно по своим блокам. Итак, на этой лекции мы рассмотрели, что такое выравнивание данных, мы узнали, почему при перестановке полей в структуре меняется ее размер. Потому что требуется различное количество padding. Помните, что поля элементарных типов, как правило, выравниваются по границе своего размера. А еще на этой лекции мы пользовались полезным макросом "offsetof". Он возвращает смещение поля в байтах от начала структуры.