Современные компиляторы обычно не выделяют память для объектов любым простым способом.Предположим, вам дали несколько различных предметов и сказали, чтобы они хранили их на полке эффективно.Скорее всего, вы не просто положите каждый предмет на полку в том же порядке, в котором вы их получили.Вероятно, вы бы сложили аналогичные объекты (если бы они были стекируемыми) и в противном случае организовали бы объекты для эффективного использования пространства.Компиляторы делают то же самое.
Предположим, что компилятор назначит память всем объектам, определенным в функции.Вместо того, чтобы просто читать функцию и назначать память, как только он видит каждое определение, компилятор может прочитать всю функцию и запомнить информацию обо всех определениях.Затем он может организовать все объекты одинаковых размеров вместе, а затем отсортировать объекты по размерам.
Одна из причин этого заключается в том, что компьютеры часто имеют требования или преимущества выравнивания.Объекты шириной четыре байта часто должны быть расположены по адресам памяти, кратным четырем байтам.(Одна из причин этого заключается в том, что соединения между процессором и памятью и соединения внутри процессора имеют ширину четыре байта - они эффективно используют 32 провода для переноса 32 бит. Перемещение 32 бит с места на место легко, но смещение битовв единицах менее 32 бит требует дополнительных устройств внутри процессора.) Поскольку ваш вопрос не касается объектов различной ширины, я не буду вдаваться в этот аспект дальше.
Так как компилятор читает всю функцию,он должен помнить все объекты, которые вы определяете.В вашем примере это включает arr
, a
, b
и c
.Для этого компилятор использует некоторую структуру данных, чтобы запомнить их.Одна из первых структур данных, о которой вы узнаете, - это простой список.Компилятор может хранить список определенных объектов и имен.Он может хранить список в том порядке, в котором компилятор видит имена - arr
, a
, b
, c
- или он может сохранять список в алфавитном порядке - a
, arr
, b
, c
.Или он может поддерживать список в порядке по размеру или другим функциям, например, a
, b
, c
, arr
, если отсортирован по размеру.
Однако оказывается, что простые списки неэффективны,Если мы попытаемся сохранить список в алфавитном порядке, то элементы должны перемещаться каждый раз, когда мы хотим поместить новое имя в середину.Даже список, который просто хранится в том порядке, в котором мы видим имена, так что новые имена просто добавляются в конец, не требуя какого-либо перемещения, создает проблемы, когда мы хотим делать более сложные вещи с данными, например сортировать список по выравниваниютребования или размер.
Таким образом, компиляторы используют более сложные структуры данных для управления этой информацией.Когда компилятор видит определения, он вводит имена в свои структуры данных, которые могут использовать различные методы для организации данных.Позже, когда компилятор выделяет память для всех объектов, порядок их обработки является результатом того, как структура данных организовала их.Это не ясный или простой результат того, как имена появляются в вашем исходном коде.
Таким образом, в общем, нет никаких оснований ожидать, что компилятор будет выделять память в порядке, связанном с порядком, в которомимена появляются в вашем исходном коде.
Более того, в большинстве функций компилятор вообще не назначает фиксированную память многим объектам.Компилятор может хранить переменную только в регистре процессора, но не в памяти, или он может использовать другую память для переменной в разное время во время выполнения функции.В вашем примере компилятор должен назначить память для объектов, потому что вы берете их адреса.В коде, который не принимает адреса этих переменных, компилятор, скорее всего, вообще не будет хранить их в памяти - функция настолько проста, что процессор может выполнить работу, используя только регистры процессора, или даже оптимизировать код во время компиляции дляудалите часть этого.