awesome-everything EN
↑ Обратно к восхождению

Базовый CS с нуля

Параметры и возвращаемые значения

Суть Параметры — значения, скопированные в новый кадр до начала выполнения тела функции. Возвращаемое значение передаётся вызывающей стороне при срабатывании RET. Оба механизма передают данные через границу кадра стека.
◷ 20 min

Ты уже умеешь трассировать стек вызовов по мере вызова и возврата функций. Следующий вопрос: как данные попадают в функцию и как результат возвращается обратно?

Функция, которая всегда работает с одними и теми же фиксированными значениями, почти бесполезна. Настоящие функции принимают параметры — значения, предоставленные вызывающей стороной в момент вызова — и производят возвращаемое значение, которое вызывающая сторона может использовать. Эти два механизма — интерфейс между функцией и кодом, который её вызывает, и оба работают через кадр стека.

Цель

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

Идея

Параметры объявляются в сигнатуре функции. Когда вызывающая сторона выдаёт CALL, значения, которые она передаёт (называемые аргументами), копируются в новый кадр до начала выполнения тела функции. Внутри функции параметры выглядят и ведут себя точно как локальные переменные — это именованные ячейки внутри кадра. Разница в том, что они заранее заполнены значениями вызывающей стороны, а не устанавливаются присваиванием.

В синтаксисе TypeScript function add(x: number, y: number): number объявляет два параметра x и y. В точке вызова add(3, 5) значения 3 и 5 — аргументы, которые копируются в ячейки x и y нового кадра.

Возвращаемые значения работают в обратном направлении. Когда тело функции достигает оператора return, оно вычисляет возвращаемое значение и делает его доступным для вызывающей стороны. Аппаратно это обычно делается путём помещения возвращаемого значения в регистр CPU (быстрый слот хранения внутри самого CPU) до срабатывания RET. Вызывающая сторона затем читает этот регистр после того, как RET восстанавливает счётчик команд.

Ключевой момент: параметры передают данные в кадр; возвращаемое значение передаёт данные из кадра. Оба пересечения происходят на границе кадра — в момент CALL (вход) и RET (выход).

Функция для трассировки: add(x, y) принимает два числа и возвращает их сумму. Трассировка показывает add(3, 5) с состоянием стека до вызова, во время выполнения и после возврата.

Код
1 function add(x: number, y: number): number {
2 let result = x + y; // result — локальная переменная в кадре add
3 return result; // передаёт result обратно вызывающей стороне
4 }
5
6 function main(): void {
7 let total = add(3, 5); // вызывающая сторона: аргументы 3 и 5 переданы в add
8 // total теперь хранит 8
9 }
  • L1 x и y — параметры: живут в кадре add, заранее заполнены аргументами вызывающей стороны
  • L2 result — локальная переменная: кадр add теперь содержит x, y и result
  • L3 return: помещает значение result в регистр, затем срабатывает RET
  • L7 add(3,5): аргументы 3 и 5 копируются в новый кадр add как x и y
  • L8 после RET: регистр с 8 прочитан; total присваивается 8 в кадре main
add(3, 5): параметры входят в кадр add при вызове; возвращаемое значение выходит через регистр при RET.
Пошаговый разбор

Трассировка main(), которая вызывает add(3, 5). Каждая ячейка показывает содержимое одного кадра.

1 function add(x: number, y: number): number {
2 let result = x + y;
3 return result;
4 }
5
6 function main(): void {
7 let total = add(3, 5);
8 }

Почему это работает

Почему параметры копируются в кадр, а не используются совместно с вызывающей стороной? Потому что каждый вызов функции нуждается в независимом рабочем пространстве. Если бы add могла изменять 3 и 5 вызывающей стороны, вызвать add дважды с разными аргументами было бы невозможно осмыслить. Копирование значений означает, что кадр вызываемой функции полностью самодостаточен — она может свободно изменять ячейки x и y, не затрагивая ничего в кадре вызывающей стороны. Это поведение «копировать при входе» называется передачей по значению и является стандартом в большинстве языков для примитивных значений, таких как числа.

Практика 0 / 5

function multiply(a: number, b: number): number { return a * b; } — Сколько параметров у multiply?

Вызывается multiply(4, 6). Какое значение возвращает multiply?

После возврата multiply(4, 6) сколько кадров стека на стеке (при условии, что она была вызвана из main и ничего больше не активно)?

function square(n: number): number { return n * n; } — Вызывается square(7). Какое значение помещается в регистр возврата?

function add(x: number, y: number): number { let r = x + y; return r; } — Кадр add содержит x, y и r. Сколько именованных ячеек содержит кадр add?

Проверь себя
Викторина

При вызове add(3, 5), где живут значения 3 и 5 во время выполнения add?

Итог

Параметры — именованные ячейки в кадре стека функции, заранее заполненные аргументами (значениями), которые вызывающая сторона передала в момент вызова. Внутри функции параметры ведут себя точно как локальные переменные. Значения копируются в кадр (передача по значению для примитивов), поэтому у вызываемой функции есть независимое рабочее пространство. Возвращаемое значение производится оператором return: значение помещается в регистр CPU, срабатывает RET, кадр снимается, и вызывающая сторона читает возвращаемое значение из регистра. Параметры несут данные в кадр при CALL; возвращаемое значение несёт данные из кадра при RET.

Продолжить восхождение ↑Область видимости
хоткеи развернуть
поиск
K
пред. пьеса
k
след. пьеса
j
тиры
t
это меню
?
sources3
expand
  1. 01
  2. 02
  3. 03

Trademarks belong to their respective owners. Editorial reference only.