разница между jump и call

<p>чем отличаются инструкции jump и call? как это относится к понятиям более высокого уровня, таким как goto или вызов процедуры? (я прав в сравнении?)</p><p>вот что я думаю:</p><p>jump или goto - это перенос элемента управления в другое место, и элемент управления не возвращается автоматически в точку, откуда он вызывается.</p><p>с другой стороны, вызов call или процедуры/функции возвращается к точке, откуда он вызывается. из-за этого различия в их природе языки обычно используют стек, а кадр стека выдвигается, чтобы "запомнить" место для возврата для каждой вызванной процедуры. это поведение относится и к рекурсивным процедурам. в случае хвостовой рекурсии, однако, нет необходимости "выдвигать" кадр стека для вызова <em>каждого</em>.</p><p>ваши ответы и комментарии будут высоко оценены.</p>

<p>вы в основном правы, если говорите о call/jmp в сборке x86 или о чем-то подобном. основным отличием является:</p><ul> <li>jmp выполняет переход к локации, ничего не делая</li> <li>call помещает в стек текущий указатель инструкций (точнее: один после текущей инструкции), а затем jmp в местоположение. с ret вы можете вернуться туда, где вы были.</li> </ul><p>обычно call - это просто удобная функция, реализованная с использованием jmp. вы могли бы сделать что-то вроде</p><pre><code> movl $afterjmp, -(%esp) jmp location afterjmp: </code></pre><p>вместо звонка.</p>

<p>вы совершенно правы относительно разницы между прыжком и коллом.</p><p>в примере с одной функцией с хвостовой рекурсией компилятор может повторно использовать существующий кадр стека. однако, это может стать более сложным с взаимно рекурсивными функциями:</p><pre><code>void ping() { printf("ping\n"); pong(); } void pong() { printf("pong\n"); ping(); } </code></pre><p>рассмотрим случай, когда ping () и pong () являются более сложными функциями, которые принимают разное количество параметров. <a href="http://www.complang.tuwien.ac.at/schani/diplarb.ps">статья марка пробста</a> подробно описывает реализацию хвостовой рекурсии для gcc.</p>

<p>я думаю, у вас есть общая идея.</p><p>это зависит от архитектуры, но в целом от аппаратного уровня:</p><ul> <li> <p>инструкция перехода изменит <a href="http://en.wikipedia.org/wiki/program\_counter">счетчик программы</a> , чтобы продолжить выполнение в другой части программы.</p> </li> <li> <p>инструкция вызова переведет текущее местоположение программы (или текущее местоположение + 1) в <a href="http://en.wikipedia.org/wiki/call\_stack">стек вызовов</a> и перейдет к другой части программы. инструкция возврата затем вытолкнет местоположение из стека вызовов и вернется к исходному положению (или исходному положению + 1).</p> </li> </ul><p>таким образом, инструкция перехода близка к <code>goto</code>, а инструкция вызова близка к процедурному/функциональному вызову.</p><p>кроме того, по причине того, что стек вызовов используется при выполнении вызовов функций, добавление слишком большого количества адресов возврата в стек вызовов посредством рекурсии вызовет <a href="http://en.wikipedia.org/wiki/stack\_overflow">переполнение стека</a> .</p><p>когда я изучаю ассемблер, мне легче работать с процессорами <a href="http://en.wikipedia.org/wiki/reduced\_instruction\_set\_computer">risc</a> , чем с процессорами x86, так как в нем меньше инструкций и более простых операций.</p>

<p>одна поправка к вашим мыслям: не только с tail <em>recursion</em>, но, как правило, с tail <em>calls</em>, нам не нужен стековый фрейм, и, следовательно, мы можем просто использовать jmp там ( при условии, что аргументы были установлены правильно).</p>