Задача 7.1. Передача в функцию параметров стандартных типов
Написать программу вывода таблицы значений функции Ch x (гиперболический косинус) для аргумента, изменяющегося в заданных пределах с заданным шагом. Значения функции вычислять с помощью разложения вряд Тейлора с точностью ∊.
На втором семинаре мы уже рассматривали подобные задачи (см. задачи 2.4 и 2.5;, поэтому принцип вычисления суммы ряда вам уже знаком. Алгоритм работы программы также приводился ранее; для каждого из серии значений аргумента вычисляется и затем выводится на экран значение функции. Очевидно, что подсчет суммы ряда для одного значения аргумента логично оформить в виде отдельно функции.
ВНИМАНИЕ Разработка любой функции ведется в том же порядке, что и разработка программы в целом. Сначала определяется интерфейс функции, то есть какие значения подаются ей на вход и что должно получиться в результате. Затем продумываются структуры данных, в которых будут храниться эти и промежуточные значения; затем составляется алгоритм, программа и тестовые примеры.
Нашей функции подсчета суммы ряда требуется получить извне значение аргумента и точность. Пусть эти величины, а так же результат имеют тип double. Следовательно, заголовок функции может выглядеть так:
Обратившись к задаче 2.5, мы видим, что для вычисления суммы ряда понадобятся две промежуточные переменные для хранения очередного члена ряда и его номера. Эти переменные должны быть описаны внутри функции, поскольку вне ее они не нужны (вспомните, что еще на первом семинаре мы рекомендовали вам описывать переменные так, чтобы их область действия была минимальной из возможных).
Давайте рассмотрим текст программы:
Как видите, за счет использования функции программа получилась более ясной и компактной, потому что задача была разделена на две: вычисление функции и печать таблицы. Кроме того, написанную нами функцию можно при необходимости без изменений перенести в другую программу или поместить в библиотеку.
Если определение функции размешается после ее вызова, то перед функцией в которой он выполняется, размешают прототип (заголовок). Обычно заголовки всех используемых в программе функций размещают в самом начале файла или в отдельном заголовочном файле1. Заголовок нужен для того, чтобы компилятор мог проверить правильность вызова функции. Стандартные заголовочные файлы, которые мы подключаем к программам, содержат прототипы функций библиотеки именно с этой целью.
В этой программе для ввода-вывода мы применили не классы, а функции, унаследованные из библиотеки языка С, поскольку с их помощью, на наш взгляд, форматированный вывод записывается более компактно. Обратите внимание на спецификацию формата g. Она применяется для вывода вещественных чисел в широком диапазоне значений. Первое число модификатора (14) задает, как и для других спецификаций, ширину отводимого под число поля, а второе (6) - не точность, как в формате f, а количество значащих цифр. При этом число выводится либо в формате f, либо в формате е (с порядком) в зависимости от того, какой из них получится короче.
При написании нашей функции возникает проблема, как сигнализировать о том,
Во-первых, можно поступить так, как сделано в приведенной выше программе: вывести текстовое сообщение, сформировать какое-либо определенное значение функции (чаще всего это 0) и выйти из функции. Недостаток этого способа — печать диагностического сообщения внутри функции. Это нежелательно, а порой (например, когда функция входит в состав библиотеки) и вовсе недопустимо. Попробуйте задать в качестве исходных данных большие значения аргумента и высокую точность. Вы увидите, что 500 итераций для ее достижения недостаточно, и таблицу результатов «портит» сообщение о том, что ряд расходится.
Более грамотное решение — сформировать в функции и передать наружу признак
Недостатком этого метода является увеличение количества параметров функции. Да и программа, использующая функцию, тоже несколько усложнилась! Надеемся, вы обратили внимание на знак & перед параметром err. Это — признак передачи параметра по ссылке. Такой способ позволяет передавать значения из функции в вызывающую программу.
Сейчас самое время рассмотреть механизм передачи параметров в функцию. Он
Выражение вычисляется, и его результат записывается в стек на место, выделенное для соответствующего параметра.
Ссылка, синтаксически являясь синонимом имени некоторого объекта, в то же время содержит его адрес. Поэтому ссылку, в отличие от указателя, не требуется разадресовывать для получения значения объекта. Если мы передаем в функцию ссылку, то есть пишем в списке параметров выражение вида double & eps, а при вызове подставляем на его место аргумент, например eps_fact, мы тем самым передаем в функцию адрес переменной eps_fact. Этот адрес обрабатывается так же, как и остальные параметры: в стеке создается его копия. Функция, работая с копией адреса, имеет доступ к ячейке памяти, в которой хранится значение переменной eps_fact, и тем самым может его изменить. Вот и все!
Можно передать в функцию и указатель; в этом случае придется применять операции разадресации и взятия адреса явным образом. Для нашей функции применение указателя для передачи третьего параметра будет выглядеть так:
Как видите, в прототипе (и, конечно, в определении функции) явным образом указывается, что третьим параметром будет указатель на целое. При вызове на его место передается адрес переменной err. Чтобы внутри функции изменить значение этой переменной, применяется операция получения значения по адресу.
Итак, мы видим, что для входных данных функции используется передача параметров по значению, для передачи результатов ее работы - возвращаемое значение и/или передача параметров по ссылке или указателю. На самом деле у передачи по значению есть один серьезный недостаток: для размещения в стеке копии данных большого размера (например, структур, состоящих из многих полей) тратится и время на копирование, и место. Кроме того, стек может просто переполниться. Поэтому более безопасный, эффективный и грамотный способ - передавать входные данные по ссылке, да не по простой, а по константной, чтобы исключить возможность непреднамеренного изменения параметра в функции.
Для нашей программы передача входных данных по константной ссылке выглядит так:
Поскольку вопрос о передаче параметров очень важен, не поленимся повторить изложенное еще раз в краткой форме:
|