Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Lection4.doc
Скачиваний:
7
Добавлен:
17.12.2018
Размер:
153.09 Кб
Скачать

5.2 Передача имен функций и указателей через список аргументов

Зададимся целью написать функцию, которая решает произвольное нелинейное уравнение x = f(x) методом простых итераций с заданной точностью.

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

xn+1 = f(xn).

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

double iter_1(double x, double eps, double (*f)(double));

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

Более подробно посмотрим на третий формальный аргумент в заголовке функции iter_1. Сначала интерпретируется содержимое первых круглых скобок (*f) - f является адресом, далее интерпретируем вторые круглые скобки (*f)(double) - f является адресом функции, получающей один вещественный аргумент. Теперь весь аргумент целиком double (*f)(double) - f является адресом функции, получающей один вещественный аргумент и возвращающей вещественное значение типа double. Именно такая функция и определяет вид нашего нелинейного уравнения. Итак, минимальное необходимое количество аргументов мы включили в список аргументов функции iter_1.

Кроме того, мы объявили функцию iter_1 как возвращающую вещественное значение с помощью оператора return. Естественно, это возвращаемое значение будет корнем нашего уравнения.

Приведем теперь возможное описание функции iter_1:

double iter_1(double y, double eps, double (*h)(double))

{

while(fabs(y-h(y)) > eps)

{

y=h(y);

}

return y;

}

В описании этой функции первый формальный аргумент y используется в качестве первоначального приближения к корню.

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

Второй формальный параметр eps в списке аргументов используется в качестве допустимой погрешности вычисления корня.

Третий входной параметр h используется как имя функции, определяющей нелинейное уравнение. Заметим, что цикл while в функции iter_1 может закончить работу в одном случае, если | y-h(y) | < eps (найден корень уравнения y=h(y) с допустимой погрешностью eps). В любом случае функция iter_1 возвращает с помощью оператора return значение, хранящееся в переменной y - последнее найденное приближение к корню.

И все это в силу того, что функция iter_1 может вернуть лишь одно значение с помощью оператора return.

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

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

В этом случае мы сообщаем функции при вызове адреса переменных, по которым после окончания работы она, т.е. функция, оставит необходимые результаты.

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

Рассмотрим заголовок функции

void iter_2(double *z, double eps, int *err, double (*f)(double));

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

Второй аргумент - это обычное вещественное значение типа double. Назначение этого аргумента - допустимая погрешность при вычислении корня. Третий аргумент - адрес переменной целого типа.

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

Вот описание функции iter_2:

void iter_2(double *z, double eps, int *err, double (*g)(double))

{

int i=0; // i – счетчик числа итераций < 1000000

while( fabs(*z-g(*z)) > eps)

{

*z = g(*z);

++i;

if (i>1000000)

{

*err=0;

return;

}

}

*err=1;

return;

}

Обратим внимание, что первый формальный аргумент z этой функции является указателем (адресом), поэтому во всех выражениях, чтобы получить значение, хранящееся по этому адресу, используется операция разадресации *. То же замечание относится и к переменной err. Цикл while в теле функции вычисляет последовательные приближения к корню и может быть завершен в двух случаях: либо нарушено условие цикла, т.е. найден корень с требуемой точностью (код завершения 1), либо превышено максимально допустимое число итераций и вычисления прекращены (код завершения 0).

В цикле while в операторе if проверяется, не превышено ли максимально допустимое число итераций, и в зависимости от этого по адресу err заносится 1 (корень найден) или 0 (корень не найден).

Таким образом, после завершения работы функции iter_2 по двум переданным ей при вызове адресам будет занесено два вычисленных числа - последнее приближение к корню и код завершения.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]