Студопедия — Задача 7.1. Передача в функцию параметров стандартных типов
Студопедия Главная Случайная страница Обратная связь

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

Задача 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; просмотров: 789. Нарушение авторских прав; Мы поможем в написании вашей работы!



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

Обзор компонентов Multisim Компоненты – это основа любой схемы, это все элементы, из которых она состоит. Multisim оперирует с двумя категориями...

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

Важнейшие способы обработки и анализа рядов динамики Не во всех случаях эмпирические данные рядов динамики позволяют определить тенденцию изменения явления во времени...

Трамадол (Маброн, Плазадол, Трамал, Трамалин) Групповая принадлежность · Наркотический анальгетик со смешанным механизмом действия, агонист опиоидных рецепторов...

Мелоксикам (Мовалис) Групповая принадлежность · Нестероидное противовоспалительное средство, преимущественно селективный обратимый ингибитор циклооксигеназы (ЦОГ-2)...

Менадиона натрия бисульфит (Викасол) Групповая принадлежность •Синтетический аналог витамина K, жирорастворимый, коагулянт...

Плейотропное действие генов. Примеры. Плейотропное действие генов - это зависимость нескольких признаков от одного гена, то есть множественное действие одного гена...

Методика обучения письму и письменной речи на иностранном языке в средней школе. Различают письмо и письменную речь. Письмо – объект овладения графической и орфографической системами иностранного языка для фиксации языкового и речевого материала...

Классификация холодных блюд и закусок. Урок №2 Тема: Холодные блюда и закуски. Значение холодных блюд и закусок. Классификация холодных блюд и закусок. Кулинарная обработка продуктов...

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