Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
ШПОРЫ_ТЯП.docx
Скачиваний:
2
Добавлен:
01.03.2025
Размер:
1.63 Mб
Скачать

34. Распределение памяти. Стековый фрейм.

Для иллюстрации выделения памяти в стеке времени выполнения обратимся к схеме функции С, которая использовалась при изучении таблицы символов. Часть стека, необходимую одной функции, называют стековым фреймом (stack frame), и ниже показывается, как можно выделить память для фрейма, соответствующего функции scopes.

void scopes()

{int a,b,c; /*уровень 0*/

{int a,b; /*уровень la*/

}

{float c,d; /*уровень lb*/

{int m; /*уровень 2*/

}

}

}

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

Изначально стек времени выполнения пуст.

После объявления а, b, с (уровень 0) стек выглядит следующим образом.

Здесь а представляет область памяти для хранения значения переменной а и т.д. После объявления уровня 1 стек может выглядеть так.

После прохождения уровня 1а во время выполнения программы стек возвращается к предыдущему состоянию.

В начале уровня 1b он может преобразоваться к такому виду (слева).

Здесь для значений с и d типа float выделено вдвое больше памяти, чем для значений а, b и с типа int. В начале уровня 2 стек принимает вид (справа).

После выхода с уровня 2 стек становится таким (слева).

После прохождения уровня 1b стек возвращается в состояние (справа). Вновь становится пустым после завершения функции scopes.

Согласно данному выше описанию после завершения выполнения составного оператора область выделенной ему памяти стека просто освобождается. Для этого может использоваться массив указателей, каждый элемент которого указывает на основание сегмента стека, который соответствует выполняемому в данный момент составному оператору.

Для определения адреса переменной по отношению к основанию стекового фрейма необходимо всего лишь знать объем памяти, занимаемый значениями каждой из переменных, расположенных в стеке ниже рассматриваемой переменной; эта информация известна в процессе компиляции.

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

Описанная модель достаточна для удовлетворения требований к памяти одной простой функции, но не программ, содержащих множество функций, которые могут вызывать друг друга. Таким образом, требуется более общая модель. Поскольку рассматривается динамическая память, то на любом этапе выполнения программы память необходима лишь тем функциям, что используются в данный момент. Кроме того, выход из функций будет происходить в порядке, противоположном порядку их вызова, так что модель не будет сильно отличаться от уже рассмотренной. Основное отличие заключается в том, что последовательность вызовов функций, в общем случае, во время компиляции неизвестна

Рассмотрим следующий фрагмент программы на С.

main() {

first ();

second ();

}

first() {

second();

}

Как видно, функцию second() можно вызвать любым из двух способов.

  1. Непосредственно из main().

2. Из first(), которая вызывается из main().

На рисунках изображены соответствующие стеки времени выполнения. Через second() помечена область стека, соответствующая функции second(), и т.д.

Адрес переменной по отношению к основанию стекового фрейма, в котором она хранится, известен в процессе компиляции. В то же время расположение стекового фрейма по отношению к основанию стека, в общем случае, во время компиляции неизвестно и должно определяться уже во время выполнения программы. Программа на языке С имеет одно характерное свойство: во время выполнения доступ к переменным (не относится к переменным класса extern и глобальным переменным) возможен только из простой функции (которая активна в данный момент). Следовательно, если во время выполнения программы имеется указатель на начало текущего стекового фрейма, то информации о значении указателя и адреса переменной внутри секции стека (известен во время компиляции) достаточно для нахождения адреса переменной по отношению к основанию стека.

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

Как показано на рисунке, это можно осуществить с помощью массива указателей.

Для поддержания массива динамических указателей необходимы следующие действия времени выполнения программы.

    • Включение начального адреса нового стекового фрейма в массив указателей при вызове каждой новой функции.

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

В качестве альтернативы вместо использования массива указателей можно запоминать динамические указатели в самом стеке.

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

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]