Студопедия Главная Случайная страница Обратная связь

Разделы: Автомобили Астрономия Биология География Дом и сад Другие языки Другое Информатика История Культура Литература Логика Математика Медицина Металлургия Механика Образование Охрана труда Педагогика Политика Право Психология Религия Риторика Социология Спорт Строительство Технология Туризм Физика Философия Финансы Химия Черчение Экология Экономика Электроника

Задача 7.1. Передача в функцию параметров стандартных типов





 

Написать программу вывода таблицы значений функции Ch x (гиперболический косинус) для аргумента, изменяющегося в заданных пределах с заданным шагом. Значения функции вычислять с помощью разложения вряд Тейлора с точностью ∊.

 

На втором семинаре мы уже рассматривали подобные задачи (см. задачи 2.4 и 2.5;, поэтому принцип вычисления суммы ряда вам уже знаком. Алгоритм работы программы также приводился ранее; для каждого из серии значений аргумента вычисляется и затем выводится на экран значение функции. Очевидно, что подсчет суммы ряда для одного значения аргумента логично оформить в виде отдельно функции.

 

ВНИМАНИЕ

Разработка любой функции ведется в том же порядке, что и разработка программы в целом. Сначала определяется интерфейс функции, то есть какие значения подаются ей на вход и что должно получиться в результате. Затем продумываются структуры данных, в которых будут храниться эти и промежуточные значения; затем составляется алгоритм, программа и тестовые примеры.

 

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

 

double cosh(double x, double eps);

 

Обратившись к задаче 2.5, мы видим, что для вычисления суммы ряда понадобятся две промежуточные переменные для хранения очередного члена ряда и его номера. Эти переменные должны быть описаны внутри функции, поскольку вне ее они не нужны (вспомните, что еще на первом семинаре мы рекомендовали вам описывать переменные так, чтобы их область действия была минимальной из возможных).

 

Давайте рассмотрим текст программы:

 

#include < stdio.h> #include < math.h> double cosh(double x, double eps); // прототип функции int main() { double Xn, Xk, dX, eps; printf(" Enter Xn, Xk, dX, eps \n"); scanf(" %lf%lf%lf%lf", & Xn, & Xk, & dX, & eps); printf(" ---------------------------------- \n"); printf(" | X | Y |\n"); printf(" ---------------------------------- \n"); for (double x = Xn; x < = Xk; x += dX) printf(" | %9.2lf |% 14.6g |\n", x, cosh(x, eps)); printf(" ---------------------------------- \n"); return 0; } double cosh(double x, double eps) { const int MaxIter = 500; // максимальное количество итераций double ch = 1, у = ch; // первый член ряда и нач. значение суммы for (int n = 0; fabs(ch) > eps; n++) { ch*= x * x / ((2 * n + 1)*(2 * n + 2)); //член ряда у += ch; //добавление члена ряда к сумме if (n > MaxIter) { puts(" Ряд расходится! \n"); return 0; } } return у; }

 

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

 

Если определение функции размешается после ее вызова, то перед функцией в которой он выполняется, размешают прототип (заголовок). Обычно заголовки всех используемых в программе функций размещают в самом начале файла или в отдельном заголовочном файле1. Заголовок нужен для того, чтобы компилятор мог проверить правильность вызова функции. Стандартные заголовочные файлы, которые мы подключаем к программам, содержат прототипы функций библиотеки именно с этой целью.

 

В этой программе для ввода-вывода мы применили не классы, а функции, унаследованные из библиотеки языка С, поскольку с их помощью, на наш взгляд, форматированный вывод записывается более компактно. Обратите внимание на спецификацию формата g. Она применяется для вывода вещественных чисел в широком диапазоне значений. Первое число модификатора (14) задает, как и для других спецификаций, ширину отводимого под число поля, а второе (6) - не точность, как в формате f, а количество значащих цифр. При этом число выводится либо в формате f, либо в формате е (с порядком) в зависимости от того, какой из них получится короче.

 

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

 

Во-первых, можно поступить так, как сделано в приведенной выше программе: вывести текстовое сообщение, сформировать какое-либо определенное значение функции (чаще всего это 0) и выйти из функции. Недостаток этого способа — печать диагностического сообщения внутри функции. Это нежелательно, а порой (например, когда функция входит в состав библиотеки) и вовсе недопустимо. Попробуйте задать в качестве исходных данных большие значения аргумента и высокую точность. Вы увидите, что 500 итераций для ее достижения недостаточно, и таблицу результатов «портит» сообщение о том, что ряд расходится.

 

Более грамотное решение — сформировать в функции и передать наружу признак
успешного завершения подсчета суммы, который должен анализироваться в вызывающей программе. Такой подход часто применяется в стандартных функциях.
В качестве признака используется либо возвращаемое значение, которое не входит в множество допустимых (например, отрицательное число при поиске номера
элемента в массиве или ноль для указателя), либо отдельный параметр ошибки.
Обычно параметр ошибки представляет собой целую величину, ненулевые значения которой сигнализируют о различных ошибках в функции. Если ошибка может произойти всего одна, параметру можно назначить тип bool. Параметр передается в вызывающую программу и там анализируется. Для нашей задачи это решение выглядит так:

 

#include < stdio.h> #include < math.h> double cosh(double x, double eps, int & err); int main() { double Xn, Xk, dX, eps, у; int err; printf(" Enter Xn, Xk, dX, eps \n"); scanf(" %lf%lf%lf%lf", & Xn, & Xk, & dX, & eps); printf(" ---------------------------------- \n"); printf(" | X | Y |\n"); printf(" ---------------------------------- \n"); for (double x = Xn; x < = Xk; x += dX) { у = cosh(x, eps, err); if (err) printf(" |%9.2lf | Ряд расходится! |\n", x); else printf(" |%9.2lf |%14.6g |\n", x, y); } printf(" ---------------------------------- \n"); return 0; } double cosh(double x. double eps, int & err) { err = 0; const int MaxIter = 500; double ch = 1, у = ch; for (int n = 0; fabs(ch) > eps; n++) { ch *= x * x / ((2 * n + 1) * (2 * n + 2)); у += ch; if (n > MaxIter) { err = 1; return 0; } } return y; }

 

Недостатком этого метода является увеличение количества параметров функции. Да и программа, использующая функцию, тоже несколько усложнилась! Надеемся, вы обратили внимание на знак & перед параметром err. Это — признак передачи параметра по ссылке. Такой способ позволяет передавать значения из функции в вызывающую программу.

 

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

 

y = cosh(x + 0.2, eps / 100, еrr);

 

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

 

Ссылка, синтаксически являясь синонимом имени некоторого объекта, в то же время содержит его адрес. Поэтому ссылку, в отличие от указателя, не требуется разадресовывать для получения значения объекта. Если мы передаем в функцию ссылку, то есть пишем в списке параметров выражение вида double & eps, а при вызове подставляем на его место аргумент, например eps_fact, мы тем самым передаем в функцию адрес переменной eps_fact. Этот адрес обрабатывается так же, как и остальные параметры: в стеке создается его копия. Функция, работая с копией адреса, имеет доступ к ячейке памяти, в которой хранится значение переменной eps_fact, и тем самым может его изменить. Вот и все!

 

Можно передать в функцию и указатель; в этом случае придется применять операции разадресации и взятия адреса явным образом. Для нашей функции применение указателя для передачи третьего параметра будет выглядеть так:

 

// прототип функции: double cosh(double x, double eps, int *еrr); ... // вызов функции: у = cosh(x, eps, & еrr); // & - взятие адреса ... // обращение к еrr внутри функции: *егг = 0; // * - разадресация

 

Как видите, в прототипе (и, конечно, в определении функции) явным образом указывается, что третьим параметром будет указатель на целое. При вызове на его место передается адрес переменной err. Чтобы внутри функции изменить значение этой переменной, применяется операция получения значения по адресу.

 

Итак, мы видим, что для входных данных функции используется передача параметров по значению, для передачи результатов ее работы - возвращаемое значение и/или передача параметров по ссылке или указателю. На самом деле у передачи по значению есть один серьезный недостаток: для размещения в стеке копии данных большого размера (например, структур, состоящих из многих полей) тратится и время на копирование, и место. Кроме того, стек может просто переполниться. Поэтому более безопасный, эффективный и грамотный способ - передавать входные данные по ссылке, да не по простой, а по константной, чтобы исключить возможность непреднамеренного изменения параметра в функции.

 

Для нашей программы передача входных данных по константной ссылке выглядит так:

 

// прототип функции: double cosh(const double & х, const double & eps, int & err); ... // вызов функции: y = cosh(x, eps, err); // обречение к x и eps внутри функции не изменяется

 

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

 







Дата добавления: 2014-11-10; просмотров: 819. Нарушение авторских прав; Мы поможем в написании вашей работы!




Шрифт зодчего Шрифт зодчего состоит из прописных (заглавных), строчных букв и цифр...


Картограммы и картодиаграммы Картограммы и картодиаграммы применяются для изображения географической характеристики изучаемых явлений...


Практические расчеты на срез и смятие При изучении темы обратите внимание на основные расчетные предпосылки и условности расчета...


Функция спроса населения на данный товар Функция спроса населения на данный товар: Qd=7-Р. Функция предложения: Qs= -5+2Р,где...

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

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

Деятельность сестер милосердия общин Красного Креста ярко проявилась в период Тритоны – интервалы, в которых содержится три тона. К тритонам относятся увеличенная кварта (ув.4) и уменьшенная квинта (ум.5). Их можно построить на ступенях натурального и гармонического мажора и минора.  ...

Алгоритм выполнения манипуляции Приемы наружного акушерского исследования. Приемы Леопольда – Левицкого. Цель...

ИГРЫ НА ТАКТИЛЬНОЕ ВЗАИМОДЕЙСТВИЕ Методические рекомендации по проведению игр на тактильное взаимодействие...

Реформы П.А.Столыпина Сегодня уже никто не сомневается в том, что экономическая политика П...

Studopedia.info - Студопедия - 2014-2024 год . (0.013 сек.) русская версия | украинская версия