Объясните концепцию стекового фрейма в двух словах
Кажется, я понял идею стека вызовов в дизайне языка программирования. Но я не могу найти (возможно, я просто недостаточно тщательно ищу) какое-либо достойное объяснение того, что такое фрейм стека.
Поэтому я хотел бы попросить кого-нибудь объяснить мне это в нескольких словах.
6 ответов
Кадр стека - это кадр данных, который помещается в стек. В случае стека вызовов кадр стека будет представлять вызов функции и данные ее аргумента.
Если я правильно помню, адрес возврата функции помещается сначала в стек, затем в аргументы и место для локальных переменных. Вместе они составляют "кадр", хотя это, скорее всего, зависит от архитектуры. Процессор знает, сколько байтов находится в каждом кадре, и соответственно перемещает указатель стека, когда кадры выталкиваются и выталкиваются из стека.
РЕДАКТИРОВАТЬ:
Существует большая разница между стеками вызовов более высокого уровня и стеком вызовов процессора.
Когда мы говорим о стеке вызовов процессора, мы говорим о работе с адресами и значениями на уровне байтов / слов в сборке или машинном коде. Существуют "стеки вызовов", когда речь идет о языках более высокого уровня, но они являются средством отладки / выполнения, управляемым средой выполнения, так что вы можете регистрировать, что пошло не так с вашей программой (на высоком уровне). На этом уровне такие вещи, как номера строк, имена методов и классов, часто известны. К тому времени, когда процессор получает код, он совершенно не имеет понятия об этих вещах.
Если вы хорошо понимаете стек, то вы поймете, как работает память в программе, и если вы поймете, как память работает в программе, вы поймете, как хранится функция в программе, и если вы поймете, как хранится функция в программе, вы поймете, как работает рекурсивная функция, и если вы поймете, как работает рекурсивная функция, вы поймете, как работает компилятор, и если вы поймете, как работает компилятор, ваш ум будет работать как компилятор, и вы очень легко отладите любую программу
Позвольте мне объяснить, как работает стек:
Сначала вы должны знать, как функция хранится в стеке:
Кучи хранят значения динамического выделения памяти. Стек хранить автоматическое размещение и удаление значений.
Давайте разберемся с примером:
def hello(x):
if x==1:
return "op"
else:
u=1
e=12
s=hello(x-1)
e+=1
print(s)
print(x)
u+=1
return e
hello(4)
Теперь разберитесь в частях этой программы:
Теперь давайте посмотрим, что такое стек и что такое части стека:
Выделение стека:
Помните одно: если какая-либо функция получит "return", независимо от того, загружены ли все его локальные переменные или что-то, что она немедленно вернет из стека, будет его стековым фреймом. Это означает, что когда любая рекурсивная функция получает базовое условие, и мы ставим return после базового условия, чтобы базовое условие не дожидалось загрузки локальных переменных, находящихся в "остальной" части программы, она немедленно возвращает текущий кадр из стека и теперь, если один кадр возвращать следующий кадр в записи активации. Смотрите это на практике:
Выделение блока:
Так что теперь, когда функция находит оператор возврата, она удаляет текущий кадр из стека.
при возврате из стека значение вернется в обратном порядке, в котором они размещены в стеке.
Это очень краткое описание, и если вы хотите узнать больше о стеке и двойной рекурсии, прочитайте два поста этого блога:
Быстрое завершение. Может быть, у кого-то есть лучшее объяснение.
Стек вызовов состоит из 1 или нескольких стековых кадров. Каждый кадр стека соответствует вызову функции или процедуры, которая еще не завершена с возвратом.
Чтобы использовать кадр стека, поток сохраняет два указателя, один из которых называется указателем стека (SP), а другой - указателем кадра (FP). SP всегда указывает на "вершину" стека, а FP всегда указывает на "вершину" кадра. Кроме того, поток также поддерживает программный счетчик (ПК), который указывает на следующую команду, которая будет выполнена.
В стеке хранятся следующие данные: локальные переменные и временные значения, фактические параметры текущей инструкции (процедура, функция и т. Д.)
В отношении очистки стека существуют разные соглашения о вызовах.
"Стек вызовов состоит из кадров стека..." - Википедия
Кадр стека - это то, что вы кладете в стек. Это структуры данных, которые содержат информацию о подпрограммах для вызова.
У программистов могут возникнуть вопросы о фреймах стека не в широком смысле (что это единая сущность в стеке, которая обслуживает только один вызов функции и сохраняет адрес возврата, аргументы и локальные переменные), но в узком смысле - когда термин stack frames
упоминается в контексте параметров компилятора.
Независимо от того, имел в виду автор вопроса или нет, но концепция стекового фрейма с точки зрения опций компилятора - очень важная проблема, которая здесь не рассматривается в других ответах.
Например, компилятор Microsoft Visual Studio 2015 C/C++ имеет следующий параметр, связанный с stack frames
:
- / Oy (Frame-Pointer Omission)
GCC имеют следующее:
- -fomit-frame-pointer (Не хранить указатель кадра в регистре для функций, которые в нем не нуждаются. Это позволяет избежать инструкций по сохранению, настройке и восстановлению указателей кадра; также делает дополнительный регистр доступным во многих функциях)
Компилятор Intel C++ имеет следующее:
- -fomit-frame-pointer (Определяет, используется ли EBP как регистр общего назначения при оптимизации)
который имеет следующий псевдоним:
- / Oy
Delphi имеет следующую опцию командной строки:
- - $ W + (Создание кадров стека)
В этом конкретном смысле, с точки зрения компилятора, кадр стека - это просто код входа и выхода для подпрограммы, который вставляет привязку к стеку - который также может использоваться для отладки и обработки исключений. Инструменты отладки могут сканировать данные стека и использовать эти якоря для обратного отслеживания, находя call sites
в стеке, то есть для отображения имен функций в том порядке, в котором они были вызваны иерархически. Для архитектуры Intel это push ebp; mov ebp, esp
или же enter
для входа и mov esp, ebp; pop ebp
или же leave
для выхода.
Вот почему для программиста очень важно понять, что такое стековый фрейм, когда дело доходит до опций компилятора - потому что компилятор может контролировать, генерировать этот код или нет.
В некоторых случаях кадр стека (код входа и выхода для подпрограммы) может быть пропущен компилятором, и переменные будут напрямую доступны через указатель стека (SP/ESP/RSP), а не через удобный базовый указатель (BP/ESP/ РСП). Условия пропуска стекового фрейма, например:
- функция является конечной (то есть конечной сущностью, которая не вызывает другие функции);
- не существует try/finally или try / кроме или аналогичных конструкций, т.е. не используются исключения;
- никакие процедуры не вызываются с исходящими параметрами в стеке;
- функция не имеет параметров;
- функция не имеет встроенного кода сборки;
- так далее...
Пропуск стековых фреймов (код входа и выхода для подпрограммы) может сделать код меньше и быстрее, но это может также негативно повлиять на способность отладчиков отслеживать данные в стеке и отображать их для программиста. Это параметры компилятора, которые определяют, при каких условиях функция должна иметь код входа и выхода, например: (a) всегда, (b) никогда, (c) при необходимости (с указанием условий).
Кадр стека - это упакованная информация, связанная с вызовом функции. Эта информация обычно включает аргументы, передаваемые в функцию, локальные переменные и куда возвращаться после завершения. Запись активации - это другое имя стекового фрейма. Расположение кадра стека определяется в ABI производителем, и каждый компилятор, поддерживающий ISA, должен соответствовать этому стандарту, однако схема расположения может зависеть от компилятора. Обычно размер кадра стека не ограничен, но существует концепция, называемая "красная / защищенная зона", позволяющая системным вызовам... и т. Д. Выполняться без вмешательства в кадр стека.
Всегда есть SP, но на некоторых ABI (например, ARM и PowerPC) FP не обязателен. Аргументы, которые нужно было поместить в стек, можно сместить только с помощью SP. Будет ли сгенерирован кадр стека для вызова функции или нет, зависит от типа и количества аргументов, локальных переменных и от того, как вообще доступны локальные переменные. В большинстве ISA, во-первых, используются регистры, и если имеется больше аргументов, чем регистров, выделенных для передачи аргументов, они помещаются в стек (например, x86 ABI имеет 6 регистров для передачи целочисленных аргументов). Следовательно, иногда некоторые функции не нуждаются в размещении кадра стека в стеке, просто адрес возврата помещается в стек.