Задание № 25 · ЕГЭ

Анализ подпрограмм

Параметры, локальные переменные, возвращаемые значения и граф вызовов
6 мин чтенияСложность: Обновлено 29 сентября 2026

Анализ подпрограммы — это пошаговое прослеживание того, какие значения получают параметры и локальные переменные, в каком порядке выполняются вызовы и какое значение возвращается. Такой способ позволяет надёжно решать задания, где нужно определить результат работы программы или последовательность вызовов.

Что происходит при вызове подпрограммы

Подпрограмма — именованный фрагмент программы, который можно вызвать из другой части программы. Перед анализом полезно повторить устройство процедуры и функции: процедура обычно выполняет действие, а функция дополнительно возвращает результат.

D
Подпрограмма и вызов

Вызов подпрограммы — передача управления её заголовку с конкретными аргументами. При вызове фактические параметры сопоставляются с формальными, создаются локальные переменные, затем выполняются команды тела. После завершения управление возвращается в точку вызова.

Формальный параметр записан в объявлении подпрограммы, а фактический параметр — выражение или переменная, указанная при вызове. Например, в f(2 * x) выражение 2 * x является фактическим параметром, а имя a в function f(a) — формальным.

ЭтапЧто проверять
1. ВходКакие значения переданы параметрам
2. Локальная областьКакие переменные созданы внутри подпрограммы
3. ВыполнениеВ каком порядке идут операторы и вложенные вызовы
4. ВыходЧто изменилось и какое значение возвращено
5. ПродолжениеС какой команды возобновляется вызывающая программа

Если параметр передаётся по значению, подпрограмма получает копию. Изменение этой копии не меняет исходную переменную. При передаче по ссылке параметр связан с исходной переменной, поэтому присваивание внутри подпрограммы может изменить её.

\[\text{результат вызова} = F(\text{значения фактических параметров})\]

Области видимости и локальные переменные

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

T
Правило локальной области

При чтении имени сначала ищут переменную в текущей подпрограмме. Если её нет, проверяют внешнюю или глобальную область. Локальная переменная с совпадающим именем скрывает внешнюю глобальную переменную.

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

!
Частая ошибка

Не переносите значение локальной переменной из одного вызова в другой. Даже если подпрограмма вызывается дважды с одинаковым именем локальной переменной, это два разных экземпляра переменной, если только язык явно не задаёт сохранение состояния.

Порядок выполнения и граф вызовов

Структура программы задаёт порядок команд: сначала выполняется очередной оператор, затем — вызванная им подпрограмма. Если функция A вызывает B, а B вызывает C, выполнение идёт по цепочке A → B → C, а после завершения C управление возвращается в B, затем в A.

D
Граф вызовов

Граф вызовов — схема, в которой вершины соответствуют подпрограммам, а направленная дуга \(A \to B\) означает: подпрограмма \(A\) может вызвать подпрограмму \(B\). Граф показывает связи между подпрограммами, но не всегда полностью задаёт порядок: если два вызова идут последовательно, их порядок нужно прочитать в тексте тела.

ABCD
Граф вызовов: A вызывает B и C, а B и C вызывают D.

Для ручного решения полезно строить не только граф, но и стек вызовов. В него записывают текущую цепочку, например A → B → D. Возвращаясь из D, удаляют последний элемент и продолжают B с команды после вызова D.

Микро-проверка

Функция A вызывает B, затем C. Внутри B вызывается D. Каков порядок первого полного прохода при последовательном выполнении?

Алгоритм трассировки

В экзаменационном задании не пытайтесь сразу вычислить всё выражение. Разделите работу на состояния и фиксируйте каждое изменение. Особенно важно различать присваивание и возврат: команда x := f(a) сначала полностью выполняет f(a), а затем записывает возвращённое значение в x.

  1. Выпишите начальные значения всех переменных.
  2. Найдите первый выполняемый вызов и передайте ему фактические значения.
  3. Создайте отдельную запись параметров и локальных переменных этого вызова.
  4. Выполняйте команды сверху вниз, раскрывая вложенные вызовы только в момент их появления.
  5. Запишите результат return или присваивания имени функции.
  6. Вернитесь к вызывающей подпрограмме и продолжите со следующего оператора.
  7. После завершения основного вызова определите требуемый вывод или итоговую переменную.
Удобная таблица трассировки

Для сложного кода используйте столбцы: вызов, параметры, локальные переменные, действие, возврат. Номер вызова помогает не перепутать два одинаковых обращения к одной функции.

Разобранный пример

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

Псевдокод
1function F(a)
2    b := a + 1
3    if a > 1 then
4        return b + F(a - 2)
5    else
6        return b
7
8x := F(5)
№
Что требуется проследить

Нужно найти значение x, не потеряв параметры разных рекурсивных вызовов. Каждый вызов функции получает собственные локальные переменные.

1
Первый вызов получает параметр 5 и создаёт свою локальную переменную.
\(\displaystyle F(5):\quad a=5,\ b=5+1=6\)
2
Так как \(5>1\), вызывается вложенный экземпляр функции с параметром \(5-2=3\).
F(5)=6+F(3)
3
Во втором вызове переменная \(b\) создаётся заново и равна \(3+1=4\).
\(\displaystyle F(3):\quad a=3,\ b=4,\quad F(3)=4+F(1)\)
4
Вызов с параметром 1 создаёт ещё одну переменную \(b\). Условие \(1>1\) ложно, поэтому рекурсия заканчивается.
\(\displaystyle F(1):\quad a=1,\ b=2,\quad F(1)=2\)
5
Подставляем результат последнего вызова во второй, затем в первый.
\(\displaystyle F(3)=4+2=6,\qquad F(5)=6+6=12\)
6
Возвращённое значение присваивается переменной \(x\).
x=F(5)=12
Показать краткое решение Ответ

Цепочка вызовов: F(5) → F(3) → F(1). Значения локальной переменной b: 6, 4, 2. Возвраты идут в обратном порядке: 2, затем 6, затем 12. Итог: x = 12.

!
Частые ошибки при рекурсии

Не складывайте все значения b автоматически: каждое складывание происходит только там, где написано return b + F(...). Также нельзя считать, что после возврата внутреннего вызова внешний a изменился: при передаче по значению он остаётся равен своему исходному значению.

Формулы и контроль результата

Если подпрограмма возвращает результат через выражение, результат внешнего вызова вычисляется после завершения внутреннего. Для последовательных вызовов полезно записывать зависимость справа налево.

\[F(a)=\begin{cases}g(a),&\text{если условие ложно},\\h(a)+F(q(a)),&\text{если условие истинно} .\end{cases}\]

В этой записи \(g(a)\) — базовый случай, \(h(a)\) — часть результата текущего вызова, а \(q(a)\) — новый параметр. Для обычных, нерекурсивных подпрограмм аналогичный контроль выполняют по цепочке возвратов: сначала считают самый глубокий вызов, затем подставляют его результат.

Запомнить

Вызов выполняется до присваивания его результата. Локальные переменные принадлежат конкретному вызову. Возврат идёт к оператору, который следует сразу после вызова. При анализе сначала раскрывают вложенный вызов, затем продолжают внешнюю подпрограмму.

Q
Быстрый тест по теме

Быстрая проверка

~ 2 мин4 вопроса
Вопрос 1 / 4
Вопрос 1 из 4 · граф вызовов
Что показывает дуга A → B в графе вызовов?
Главное за минуту

Главное

  • Граф вызовов показывает, какие подпрограммы вызывают друг друга; порядок команд нужно читать по телу подпрограммы.
  • Для каждого вызова отдельно отслеживайте параметры и локальные переменные.
  • Вложенный вызов завершается раньше присваивания его результата и продолжения внешней подпрограммы.
  • При возврате двигайтесь по стеку вызовов в обратном порядке.
  • При передаче по значению изменения параметра не меняют исходную переменную; при передаче по ссылке могут менять.