Вопрос задан 25.09.2025 в 11:30. Предмет Информатика. Спрашивает Кинерейш Полина.

Какие существуют способы передачи данных между подпрограммой и вызывающей её программой?

Перейти к ответам

Ответы на вопрос

Отвечает Трубина Алёна.

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

1) Через параметры (аргументы)

Это главный канал «туда» и «обратно». Семантика бывает разной:

  • По значению (call by value)
    В подпрограмму копируется значение аргумента. Меняя локальную копию, вы не влияете на вызывающего.
    Пример: int f(int x) в C/C++ — x внутри независим.

  • По ссылке / по адресу (call by reference / by pointer)
    В подпрограмму передаётся не копия значения, а «доступ» к исходной переменной/объекту. Изменения видны снаружи.
    Пример: void inc(int& x) в C++ или void inc(int* px) в C — меняют исходный x.

  • По результату (out)
    Параметр используется только для вывода: начальное значение не читается, а в конце подпрограмма «кладёт» результат обратно. Встречается, например, в Ada, Pascal (var иногда трактуют как inout, но есть и чистые out).

  • По значению-результату (copy-in / copy-out)
    На вход — копия значения; на выход — копия обратно в исходную переменную. Если было два параметра, указывающих на одну и ту же переменную, возможны тонкие эффекты перезаписи.

  • По имени (call by name)
    Историческая семантика Algol-60: выражение подставляется «как есть» и вычисляется каждый раз заново в теле подпрограммы. В современных языках почти не используется.

  • По необходимости / лениво (call by need)
    Выражение вычисляется только при первом обращении и кэшируется. Типично для чисто функциональных языков.

  • По разделяемости (call by sharing)
    В интерпретируемых OO-языках (Python, Java) передаётся ссылка на объект по значению. Это значит: переназначить параметр внутри нельзя (вызвавший не увидит), но мутировать сам объект можно — и это увидят.
    Пример (Python): def add(a): a.append(1) изменит список снаружи; а a = a + [1] — нет.

  • Константная ссылка / read-only view
    Даёт эффективность без разрешения менять объект (C++ const T&, Rust заимствование &T).

Практические приёмы на параметрах:

  • Выходные параметры (out/inout) — когда нужно вернуть несколько значений без упаковки в кортеж/структуру.

  • Передача буфера (адрес + размер) — когда вызывающая сторона управляет памятью.

2) Через возвращаемое значение функции

Классический способ «обратно». Возможности зависят от языка:

  • Возврат скаляров, структур, кортежей/множественных значений.

  • Возврат «результата + код ошибки/исключение» (см. ниже).

  • Итераторы/генераторы — потоковая передача данных сериями (yield), когда результата много или он ленивый.

3) Через побочные эффекты

Иногда данные «просачиваются» не напрямую через сигнатуру:

  • Изменение внешнего состояния
    Глобальные/статические переменные, поля синглтонов, базы данных, кэш. Удобно, но снижает прозрачность и тестируемость.

  • Замыкания и не-локальные переменные
    Подпрограмма (лямбда) может читать/менять переменные из внешней области видимости (если язык позволяет nonlocal/ref-захват).

  • Мутация разделяемых объектов
    Если и вызывающий, и подпрограмма держат ссылку на один и тот же контейнер/объект, подпрограмма может его поменять, и это будет «каналом» передачи.

  • Исключения
    Формально — канал передачи информации об ошибках/исключительных ситуациях (тип, сообщение, данные внутри исключения).

  • Логи/события/коллбеки
    Подпрограмма генерирует события или пишет в лог, а вызывающий считывает. Это тоже обмен данными, хоть и асинхронный по духу.

(Опционально) Реализация на уровне платформы

Это не «семантика», а как аргументы реально попадают внутрь: через регистры, стек, иногда через выделенные области памяти (ABI/соглашение о вызовах). Возвращаемые значения нередко идут через специальный регистр. Для проектирования API обычно не важно, но влияет на производительность.

(Отдельный случай) Если подпрограмма в другом потоке/процессе

Это уже межпоточное/межпроцессное взаимодействие:

  • Общая память (shared memory) + синхронизация (мьютексы, атомики).

  • Очереди/каналы/сообщения (message passing, CSP-каналы, почтовые ящики Erlang).

  • IPC/сетевые механизмы: пайпы, сокеты, файлы, RPC/gRPC.

Формально это не «между подпрограммой и вызывающей», а «между сущностями исполнения», но на практике встречается часто.


Что выбирать на практике

  • Нужен один-два результата → возвращаемое значение (или кортеж/структура).

  • Нужно модифицировать переданный объект → по ссылке/указателю/«by sharing» с чётким контрактом мутаций.

  • Много результатов → структура/класс результата или выходные параметры.

  • Большие данные без копий → ссылки/указатели/константные ссылки.

  • Ошибки → исключения или тип «результат/ошибка».

  • Параллелизм → каналы/очереди (предпочтительнее, чем разделяемое состояние, ради простоты и безопасности).

Похожие вопросы

Топ вопросов за вчера в категории Информатика

Последние заданные вопросы в категории Информатика

Задать вопрос