Разница между JUMP и CALL
Как отличается инструкция JUMP и CALL? Как это связано с концепциями более высокого уровня, такими как GOTO или вызов процедуры? (Правильно ли я в сравнении?)
Вот что я думаю:
JUMP или GOTO - это передача элемента управления в другое место, и элемент управления автоматически не возвращается в точку, из которой он вызывается.
С другой стороны, вызов CALL или процедуры/функции возвращается в точку, из которой он вызывается. Из-за этой разницы в их характере языки обычно используют стек, а кадр стека толкается, чтобы "запомнить" место для возврата для каждой вызванной процедуры. Это относится и к рекурсивным процедурам. В случае хвостовой рекурсии, однако, нет необходимости "нажимать" кадр стека для каждого вызова.
Ваши ответы и комментарии будут высоко оценены.
Ответы
Ответ 1
В основном вы правы, если вы говорите о CALL/JMP в сборке x86 или что-то подобное. Основное отличие:
- JMP выполняет переход к местоположению без каких-либо действий
- CALL выталкивает текущий указатель инструкции в стеке (скорее: один после текущей команды), а затем JMP в местоположение. С помощью RET вы можете вернуться туда, где вы были.
Обычно CALL - это просто функция удобства, реализованная с использованием JMP. Вы могли бы сделать что-то вроде
movl $afterJmp, -(%esp)
jmp location
afterJmp:
вместо CALL.
Ответ 2
Вы точно знаете разницу между прыжком и вызовом.
В примере с одной функцией с хвостовой рекурсией компилятор может повторно использовать существующий фрейм стека. Однако он может усложниться с помощью взаимно рекурсивных функций:
void ping() { printf("ping\n"); pong(); }
void pong() { printf("pong\n"); ping(); }
Рассмотрим случай, когда ping() и pong() являются более сложными функциями, которые принимают различное количество параметров. Документ Марка Пробста подробно рассказывает о реализации хвостовой рекурсии для GCC.
Ответ 3
Думаю, у вас есть общая идея.
Это зависит от архитектуры, но в целом на аппаратном уровне:
-
Команда перехода изменит счетчик программ, чтобы продолжить выполнение в другой части программы.
-
Команда вызова выведет текущее местоположение программы (или текущее местоположение + 1) в стек вызовов и перейдет к другому частью программы. После этого команда возврата выведет местоположение из стека вызовов и вернется в исходное местоположение (или местоположение orignal + 1).
Итак, команда перехода близка к GOTO
, а команда вызова близка к процедурно-функциональному вызову.
Кроме того, по причине того, что при выполнении вызовов функций используется стек вызовов, нажатие слишком большого количества обратных адресов в стек вызовов путем рекурсии вызывает stack переполнение.
При сборе сборки мне легче справляться с RISC, чем процессоры x86, поскольку имеет тенденцию иметь меньше инструкций и более простые операции.
Ответ 4
Одна поправка к вашим мыслям: это не только с хвостовой рекурсией, но, как правило, с хвостовыми вызовами, нам не нужен фрейм стека, и, следовательно, просто JMP там (если аргументы настроены правильно).
Ответ 5
В соответствии с микропроцессором, во-первых, условие проверяется, а затем выполняет операции перехода (переходит к другому коду) и не возвращается.
Операция вызова похожа на функцию callig на языке c
, и когда функция выполняется, она возвращается обратно, чтобы завершить ее выполнение.