Почему параметры функции помещаются в стек вызовов раньше, чем адрес возврата?

Из http://en.wikipedia.org/wiki/Stack_pointer#Structure

введите здесь описание изображения

Мне интересно, почему адрес возврата для функции помещается выше параметров этой функции?

Более разумно помещать адрес возврата в стек перед параметрами для Drawline, потому что параметры больше не требуются, когда адрес возврата выталкивается для возврата к вызывающей функции.

Каковы причины предпочтения реализации, показанной на диаграмме выше?


person Lazer    schedule 12.02.2012    source источник
comment
Имейте в виду, что такое расположение не является частью стандарта C++. Он специфичен для отдельного процессора, компилятора и/или операционной среды. Ситуация, описанная в этой статье, типична и точна для GCC/X86, но ни в коем случае не универсальна.   -  person Robᵩ    schedule 12.02.2012
comment
Количество как параметров, так и локальных переменных может быть не фиксированным. Как можно было определить адрес всех 3, если обратный адрес не был посередине?   -  person Pubby    schedule 12.02.2012
comment
@Pubby: у меня нет ответа, но такая ситуация также происходит с Locals DrawSquare и Parameters DrawLine здесь.   -  person Lazer    schedule 12.02.2012
comment
Это просто естественный порядок выполнения. Вызывающий сначала помещает аргументы, затем CALL, затем функция настраивает указатель стека для локальных переменных. Делать это любым другим способом было бы просто неэффективно.   -  person Hans Passant    schedule 12.02.2012


Ответы (2)


Обратный адрес обычно передается с помощью машинной команды call [которая в наборе инструкций на родном языке] в то время как параметры и переменные передаются несколькими машинными командами, которые создает компилятор.

Таким образом, адрес возврата — это последнее, что выдвигает вызывающая программа, и перед тем, что [локальные переменные] выдвигает вызываемая сторона.

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

Кроме того, еще одна причина заключается в том, что вызывающий абонент выделяет место в стеке для параметров - он [вызывающий] также должен очищать его.

person amit    schedule 12.02.2012
comment
Он [звонящий] также должен быть тем, кто его убирает. - на самом деле, почти в любом соглашении о вызовах, которое не поддерживает вариативные параметры (например, stdcall), ответственность за очистку лежит на вызываемом объекте, чтобы уменьшить размер исполняемого файла (код для очистки пишется только один раз в конце функции вместо одного раза). для каждого вызова функции). - person Matteo Italia; 12.02.2012

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

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

Также обратите внимание, что это не зависит от вопроса о том, кто очищает параметры. В принципе, вызываемая функция может вызывать деструкторы вызовов аргументов, даже если физически они лежат во фрейме стека вызывающего объекта. Кроме того, многие процессоры (включая x86) имеют инструкции, которые автоматически добавляют дополнительный пробел перед адресом возврата при возврате (например, компиляторы Паскаля обычно делали это, потому что в Паскале у вас нет никакой очистки, кроме возврата памяти, и, по крайней мере, для процессоров того времени было более эффективно выполнять очистку с помощью этой инструкции процессора (я понятия не имею, верно ли это для современных процессоров).Однако C не использовал этот механизм из-за списков аргументов переменной длины: этот механизм был неприменим, потому что вам нужно было бы знать во время компиляции, сколько дополнительного пространства освобождается, а K&R C не требовал опережающего объявления функций с переменным числом переменных (C89 делает это, но лишь немногие компиляторы используют это в своих интересах из-за совместимости со старым кодом), поэтому у вызывающей функции не было возможности узнать, нужно ли очищать аргументы, если только она не должна была делать это всегда.

person celtschk    schedule 12.02.2012