Думайте о стеке вызовов как о подносе учетных карточек. Стек начинается пустым. Единственное, что вы можете сделать, это записать новую учетную карточку и положить ее сверху или снять то, что находится сверху.
В этот лоток есть кое-что, помещенное средой выполнения, но для А сейчас давайте представим, что лоток пуст, когда main()
начинает работать.
Представьте, что каждая функция - это глава в книге. На каждой странице есть набор отдельных предложений, каждое из которых говорит вам сделать одно конкретное c действие. Одна из таких вещей может быть: «go перейти в другую главу и делать то, что в ней сказано, используя эту информацию, которая у меня есть». Когда вы дойдете до такого предложения, вам нужно будет вспомнить, как вернуться туда, где вы были, поэтому вы запишите следующее на карточке:
- В какой главе вы сейчас находитесь и в каком предложении глава, которую вы только что прочитали (например, номер страницы, номер абзаца и номер предложения в этом абзаце).
- Любая информация, с которой вы работали до того, как go вас попросили перейти к другой главе.
Вы кладете эту карточку на верхнюю часть лотка, затем переходите к другой главе и начинаете что-то делать. с этой главой, go назад к последней главе с таким результатом "затем вы снимаете карточку с верха, возвращаетесь к той главе, в которой она написана, находите предложение, следующее сразу за тем, которое указывает карточка, и продолжаете оттуда .
Вот и все, что здесь происходит. Главы - это функции. Предложения - это машинные инструкции. Все, что вы перенесете в другую главу, - это аргументы функций. Возвращаемое значение - это то, что вы берете из главы. «Материал, с которым вы работали», который вы записываете в учетную карточку, - это значение любых локальных переменных функции во время вызова функции.
Когда вы делаете рекурсивный вызов, вы все равно помещаете что-то в стеке, но вы просто go перешли к той же главе, которую уже читали - только с немного другой информацией. Когда вам нужно выйти из главы, верхняя карточка может сказать, что вы все еще находитесь в той же главе, но в другом предложении и с другой информацией.
У вас есть серия вложенных вызовов, по одному кадру стека на каждый рекурсивный вызов. Когда управление возвращается из рекурсивного вызова, вы все еще находитесь в той же функции, но с теми же значениями локальных переменных, которые были у вас до вызова.
Итак, в вашем случае, да, вы могли подумать об этом что у вас «несколько рук». Как бы глубоко вы ни рекурсивно переходили в одну и ту же функцию, вам придется продвигаться вверх по мере возврата каждого рекурсивного вызова.
Честно говоря, нет никакой разницы между рекурсивными вызовами и нерекурсивными вызовами в способе логического развития программы (за исключением случая оптимизации хвостового вызова). Компилятор и машинный код не заботятся о том, чтобы вызывалась одна и та же функция, они в любом случае выполняют один и тот же процесс.