Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


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



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

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

double cosh(double х. double eps): Мы видим, что для вычисления суммы ряда понадобятся две промежуточные переменные — для хранения очередного члена ряда и его номера. Эти переменные должны быть описаны внутри функции, поскольку вне ее они не нужны Рассмотрим текст программы:

#include < math.h>

#include < string.h>

#include < iomanip>

#include < stdlib.h>

#include < stdio.h> //0

using std:: cin;

using std:: cout;

using std:: endl;

using std:: setw;

int main(){

 

double cosh{double x. double eps);

double Xn, Xk, dX, eps;

printf(" Enter Xn. Xk. dX. eps \n" );

scanf(" %lf%lf%lf%lf%lf", & Xn, & Xk, & dX, & eps);

printf("................................ \n" );

printf(" | X | Y |\n" );

printf(" ................................\n" );

for (double x = Xn; x < = Xk; x += dX)

pr1ntf(" |%9.2lf |%14.6g |\n", x, cosh(x, eps);

printf(" ................................\n" ):

return 0;

)

double cosh(double x, double eps)

{ const int Maxlter = 500; // максимальное количество итераций

double ch = 1, у = ch; // первый чпен ряда и нач. значение суипы

for (int n = 0; fabs(ch) > eps; n++) {

ch *= х * х /((2 * n • 1)*(2 * n + 2)); // член ряда

у += ch; // добавление члена ряда к сумме

if [n > MaxIter) { puts(" Ряд расходится! \n" ); return 0; }

}

return у;

 

}

 

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

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

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

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

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

 

 

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

 

 

. Передача в функцию имени функции

Назовем функцию вывода таблицы значений print_tabl. Прежде всего надо определить ее интерфейс. Для того чтобы вывести таблицу, нашей функции потребуется знать диапазон и шаг изменения значений аргумента, а также какую, собственно, функцию мы собираемся вычислять. Всё? Нет. не все: забыли, что в функцию вычисления суммы ряда надо передавать точность, поэтому точность следует включить в список параметров вызывающей ее функции prtnt_tabl. Функция print_tabl не возвращает никакого значения, то есть перед ее именем надо указать void.

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

double (*fun)(double, double): Здесь описывается указатель по имени fun на функцию, получающую два аргумента типа double и возвращающую значение того же типа (от параметров по умолча-

Часто, если описание типа сложное, с целью улучшения читаемости программы задают для него синоним с помощью ключевого слова typedef:

typedef double (*Pfun)(double, double): В этом операторе задается тип Pfun. который можно использовать наряду со стан­дартными типами при описании переменных. Таким образом, заголовок функции печати таблицы должен иметь вид:

void print_tabl(Pfun fun, double Xn, double Xk, double dX, double eps);

Запишем теперь текст программы, сведя к минимуму диагностику ошибок (при превышении максимально допустимого количества итераций функция завершается, возвращая 0, а вызывающая программа бесстрастно выводит это значение):

#include < math.h>

#include < string.h>

#include < iomanip>

#include < stdlib.h>

#include < stdio.h> //0

using std:: cin;

using std:: cout;

using std:: endl;

using std:: setw;

typedef double(*Pfun)(const double, const double);

void print_tabl(Pfun, const double, const double Xn, const double Xk,

const double dX, const double eps);

double cosh{const double x, const double eps);

int main(){

double Xn, Xk, dX, eps;

printf(" Enter Xn. Xk. dX. eps \n" );

scanf(" %lf%lf%lf%lf%lf", & Xn, & Xk, & dX, & eps);

print_tabl(cosh, Xn, Xk, dX, eps);

 

return 0;

)

void print_tabl(Pfun, const double, const double Xn, const double Xk,

const double dX, const double eps){

printf(" Enter Xn. Xk. dX. eps \n" );

scanf(" %lf%lf%lf%lf%lf", & Xn, & Xk, & dX, & eps);

printf("................................ \n" );

printf(" | X | Y |\n" );

printf(" ................................\n" );

for (double x = Xn; x < = Xk; x += dX)

pr1ntf(" |%9.2lf |%14.6g |\n", x, cosh(x, eps);

printf(" ................................\n" ):

return 0;

 

double cosh(double x, double eps)

{ const int Maxlter = 500; // максимальное количество итераций

double ch = 1, у = ch; // первый чпен ряда и нач. значение суипы

for (int п = 0; fabs(ch) > eps; n++) {

ch *= х * х /((2 * n • 1)*(2 * n + 2)); // член ряда

у += ch; // добавление члена ряда к сумме

if [n > MaxIter) { puts(" Ряд расходится! \n" ); return 0; }

}

return у;

 

}

Функция print_labl предназначена для вывода таблицы значений любой функции, принимающей два аргумента типа double и возвращающей значение того же типа.

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

Передача одномерных массивов в функцию

Даны два массива из п целых чисел каждый. Определить, в каком из них больше положительных элементов.

Очевидно, что для решения этой задачи потребуется подсчитать количество положительных элементов в двух массивах, то есть выполнить для обоих массивов одни и те же действия. Следовательно, эти действия надо поместить в функцию. Интерфейс функции: входные данные — массив и количество его элементов, результат — количество положительных элементов в массиве. Таким образом, заголовок функ­ции должен иметь вид:

int n_posit(const int *a. const int n); Имя массива представляет собой указатель на его нулевой элемент, поэтому в функцию массивы передаются через указатели. Количество элементов в массиве должно передаваться отдельным параметром, потому что. в отличие от строк символов, использующих признак конца строки, для массивов общего вида никакого признака конца массива не существует.

#include < math.h>

#include < string.h>

#include < iomanip>

#include < stdlib.h>

#include < stdio.h> //0

using std:: cin;

using std:: cout;

using std:: endl;

using std:: setw;

#include < iostream.h>

int n_posit(const int *a. const int n); // прототип функции

int main(){

int i, n;

cout< < " Введите количество элементов: "; cin> > n;

int *a = new int(n]:

int *b= new int[n];

cout < < " Введите элементы первого массива: ";

for (i= 0; i < п; i++) cin> > a[i];

cout < < " Введите элементы второго массива: ";

for (i=0; i < n; i++) cin> > b[i];

if (n_posit(a, n) > n_posit(b, n))

cout < < " В первой попоимтельных больше" < < endl;

else 1f(n_posit(a, n) < n_poslt(b, n))

cout < < " Во второй положительных больие" < < endl;

else

cout < < " Одинановое количество" < < endl;

return 0;

}

int n_posit(const int *a. const int n) (

int count = 0;

for (int i=0; i < n; i++) if (a[i] > 0) count++;

return count; }

В этой программе место под массивы выделяется в динамической области памяти, поскольку в задании не указано конкретное количество элементов. Однако функцию n_posit можно без изменений применять и для «обычных» массивов, потому что для каждого из них имя тоже является указателем на нулевой элемент, только константным.

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

Надо сказать, что современные компиляторы обладают широкими возможностями оптимизации и сами отслеживают подобные ситуации, преобразуя код программы, но это не означает, что на эффективность своих программ вообще не надо обращать внимания. Главным же критерием при выборе варианта написания про-, граммы, тем не менее, остается простота ее структуры и читаемость.

Мы не получили выигрыша в длине приведенной выше программы, использовав для вычисления количества положительных элементов функцию, но причина это­му — лишь простота задачи. В реальных программах в виде отдельной функции оформляются более крупные законченные фрагменты. Разные авторы рекоменду­ют различные конкретные цифры, задающие длину функций, но сходятся в одном: функция должна быть не очень короткой, но и не очень длинной. Чаще всего сове­туют создавать функции длиной в один-два экрана. Впрочем, придерживаться ра­зумной умеренности бывает полезно не только при написании программ...

Задача 7.3. Передача строк в функцию

Написать программу, определяющую, сколько чисел содержится в каждой строке текстовою файла. Длина каж/кш строки не превышает 100 символов. Эту задачу можно разбить на две: ввод данных из файла и их анализ. Для каждой строки проверка выполняется отдельно, поэтому в виде функции логично офор­мить поиск и подсчет количества чисел в одной строке. На «сод функции будем подавать строку, а на выходе получать количество чисел в этой строке.

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

#include < fstream.h>

#include < ctype.h>

#include < string.h>

#include < iomanip>

#include < iostream.h>

#include < stdlib.h>

#include < stdio.h> //0

using std:: cin;

using std:: cout;

using std:: endl;

using std:: setw;

int main(){

ifstream fin(" test.txt", ios:: in | ios:: nocreate); // 6

if (! fin) { cout < < " No test.txt " < < endl; return 1; }

const int len = 101;

int i=1;

char str[len];

while (fin.getline(str, len)) { // 7

cout < < " В строке " < < i< < " содержится" < < num_num(str)< <

" чисел" < < endl;

i++;

return 0;

}

 

int num_num(const char *str){

int count=0;

while (*str){

if (isdigit(*str)& &! isdigit(*(str+1))& & *(str+1)! ='.') count++;

str++; }

return count;

 

}

 

Увеличение счетчика чисел в функции nun_num происходит каждый раз. когда за­канчивается число, то есть если после цифры стоит не цифра и не точка. Цикл.заканчивается по достижении нуль-символа

Передача двумерных массивов в функцию

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

#include < fstream.h>

int ser_equals(int **a, const int m, const int n);

int main(){

ifstream fin (" matrix.txt" , ios:: in | 1os:: nocreate);

1f (! f1n) { cout < < " Нет файла matrix.txt" < < endl; return 0; }

int m, n, i, j;

fin > > m > > n;

int **a = new int *[m]; // выделение памяти

for(i = 0; i < m; i++) a[i] = new int [n];

for (i=0; i < m; i++) // ввод массива

for (j = 0; j < n; j++) fin > > a[i][j]

int line = ser_equals(a, m.n); // вызов функции

if (line > = 0) cout< < " Самая длинная серия в строке " < < line;

else cout < < " Серий одинаковых элементов нет "; return 0: }

int ser_equals(int **а, const int m, const int n){

int i, j, count, line = -1, maxcount = 0;

for (1 =0; i < m: i++){

count = 0; //1

for (j=0; j < n - 1; j++) {

if (a[i][j] == a[i][j + 1] ) count++ //2

else { //3

if (count > maxcount) {

maxcount = count; line= i: } //4

count = 0;

}

)

if (count > maxcount) {

maxcount = count: line = i: } //5

}

return line;

}

Алгоритм работы функции прост: в каждой строке выполняется сравнение соседних элементов (оператор 2). Если они равны, мы находимся внутри серии, при этом увеличиваем ее текущую длину. Она накапливается в переменной count, ко­торая обнуляется перед обработкой каждой строки (оператор 1). Если же элемен­ты не равны, это означает либо окончание серии, либо просто одиночный элемент (оператор 3). В этом случае надо посмотреть, не является ли данная серия самой длинной из рассмотренных и, если да, то запомнить ее длину и номер строки, в ко­торой она встретилась (оператор 4). Для подготовки к анализу следующих серий в этой же строке надо обнулить счетчик count. Аналогичная проверка после цикла просмотра строки (оператор 5) выполняется для серии, которая расположена в конце строки, поскольку в этом случае ветвь el se выполняться не будет.

Если в массиве нет ни одной серии одинаковых элементов, функция вернет значе­ние, равное-1.


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-08-31; Просмотров: 484; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.052 с.)
Главная | Случайная страница | Обратная связь