Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

учебное пособие. Часть1. Информатика

.pdf
Скачиваний:
51
Добавлен:
04.06.2015
Размер:
2.87 Mб
Скачать

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

Вызов функции производится при помощи оператора вызова функции:

<имя >(<список фактических параметров>);

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

Следует заметить, что таким образом вызываются функции типа void, т. е. функции, не возвращающие никакого значения. Функции же, возвра- щающие значения типа int, float и т. д., вызываются упоминанием име- ни в выражении аналогично стандартным функциям sin, cos и другим.

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

Пусть имеется следующее описание функции: int max (int a, int b)

{

if (a>b)

return a;

}

else

return b;

 

 

Функция max может использоваться в выражении c = max(5,3);

m = max (c-d, c+d) + 2*max (c*d, c%d);

либо

printf ( "max=%d", max(c*d, c*d));

В данном примере функция возвращает значение целого типа, фактические параметры тоже целого типа. Параметры передаются по значению. При первом обращении к функции max формальный пара- метр а заменяется фактическим параметром, которому соответствует целое число 5, а формальный параметр b числом 3. Возвращает же функция число 5, и это число присваивается переменной с. При втором

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

221

ставляются в качестве формальных параметров. В четвертом случае

функция max вызывается из функции printf.

В примере pr21 треугольник задан координатами вершин. Найти

периметр треугольника. В функции l определяется расстояние между двумя точками.

//Пример pr21 #include <iostream> #include <cmath> using namespace std;

float l (float x1, float y1, float x2, float y2); int main ()

{

float x1, y1, x2, y2, x3, y3;

cout <<" \n Введите координаты точек:"; cin >> x1 >>y1 >>x2 >>y2 >>x3 >>y3; float a = l (x1, y1, x2, y2);

float b = l (x2, y2, x3, y3); float c = l (x1, y1, x3, y3);

cout << "Периметр треугольника равен " << a+b+c; return 0;

}

float l (float x1, float y1, float x2, float y2)

{

return sqrt ((x1-x2)*(x1-x2)+(y1-y2)*(y1-y2));

}

К списку параметров предъявляются следующие требования:

количество формальных параметров должно быть равно количеству фактических параметров;

порядок следования фактических и формальных параметров должен быть один и тот же;

тип каждого фактического должен совпадать с типом соответствующе- го формального параметра.

Рассмотрим пример использования функции. Даны вещественные чис- ла a, b и c. Необходимо вычислить значения x и y по формулам:

x =

a + b

,

c2 +10

 

 

y = bc2 ++a5.

222

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

//Пример pr22

float f ( float af, float bf, float cf )

{

return (af+bf)/cf;

}

int main()

{

float a, b, c, x, y;

printf ( "\n Введите a,b,c" ); scanf ( "%f%f%f", &a, &b, &c ); x = f ( a, b, c*c+10 );

y = f ( c, a, b*b+5 );

printf ( "\n x =%5.2f y = %5.2f \n", x, y ); return 0;

}

Для вызова функции здесь используется оператор присваивания. Пара- метры a, b, c*c+10 в момент первого обращения к функции фактиче- ские параметры, а параметры c, a, b*b+5 фактические параметры в мо- мент второго обращения к функции. Пользователь должен следить за по- рядком следования фактических параметров, смысл их использования зави- сит от порядка их перечисления в списке. Они подставляются вместо фор- мальных параметров af, bf, cf в заголовке функции, а затем над ними выполняются необходимые действия. Параметры в данном примере переда- ются по значению. В этом случае под af, bf, cf выделяется дополни- тельная память, и туда копируются значения соответствующих фактических параметров. Таким образом, в подпрограмме используются копии фактиче- ских параметров, что и позволяет в качестве последних применять выраже- ния. Так как при передаче параметров по значению в функции работаем с ко- пией, то изменение формального параметра не приводит к изменению соот- ветствующего фактического параметра. Если же в изменении фактического параметра возникает необходимость, то параметры передаются по указателю или ссылке.

Для этой же задачи составим программу с использованием параметров,

передаваемых по указателю (pr23).

//Пример pr23

void f ( float af, float bf ,float cf, float *yf )

{

*yf = (af+bf)/cf;

}

223

int main ()

{

float a, b, c, x, y;

printf ( "\n Введите a, b, c" ); scanf ( "%f%f%f", &a, &b, &c ); f ( a, b, c*c+10, &x );

f ( c, a, b*b+5, &y );

printf ( "\n x= %5.2f y= %5.2f \n", x, y); rerurn 0;

}

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

Третий способ передачи параметров это передача параметров по ссылке. В языке С этого способа передачи параметров не было. Понятие ссылка появилось только в С++. Ссылочные переменные описываются сле- дующим образом [11]:

<тип> &<имя> = <инициализирующее выражение>

Здесь, <тип> любой тип С++; <имя> идентификатор перемен- ной ссылочного типа; <инициализирующее выражение> выражение, определяющее местоположение в памяти. Например:

int y=16; int &x=y;

printf("x=%d\n",x);

y=12;

printf("x=%d\n",x);

Результат работы: x=16

x=12

Ссылочная переменная х определяет местоположение переменной у в памяти в инициализирующем выражении int &x=y. Переменная х обраба- тывается как "нормальная" переменная типа int. При обращении к такой переменной нет необходимости в операции снятия ссылки. Когда перемен- ной y присваивается значение 12, то переменная х принимает это новое зна- чение, поскольку она определяет местоположение переменной y в памяти.

224

Если в этот пример добавить строку х=10, то при этом значение переменной у станет равным 10. Поскольку х это ссылка на переменную у, то любое изменение переменной х приведет к изменению переменной у. Ссылочная переменная при объявлении должна быть инициализирована. Объявление int &x; недопустимо, поскольку неизвестно на что указывает переменная х. Теперь рассмотрим, как же передается параметр по ссылке на примере той же задачи, что и в программе pr24.

// Пример pr24

void f ( float af, float bf, float cf, float &yf )

{

yf = (af+bf)/cf;

}

int main ()

{

float a, b, c, x, y;

printf ( "\n Введите a, b, c" );

scanf ( "%f%f%f", &a, &b, &c ); f ( a, b, c*c+10, x );

f ( c, a, b*b+5, y );

printf ( "\n x= %5.2f y= %5.2f \n", x, y); return 0;

}

Здесь используется параметр yf, передаваемый по ссылке. Для фор- мального параметра float &yf инициализирующего выражения не требуется. Инициализирующее выражение подставляется при вызове функции. При вызо- ве функции f (a, b, c*c+10, x) инициализирующее выражение прини-

мает вид float &yf = х. При вызове функции f (a, b, c*c+10, у)

инициализирующее выражение принимает вид float &yf = у. В примере pr24 версия функции более "натуральна", нежели версия в примере pr23, так как нет необходимости в операции разыменовывания.

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

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

уравнений делятся на прямые и итерационные [9]. Первые позволяют найти решение непосредственно с помощью формул и всегда обеспечивают полу-

225

чение точного результата. В итерационных методах задается процедура ре- шения в виде многократного применения некоторого алгоритма. Полученное решение всегда является приближенным, хотя может быть сколько угодно близким к точному. Итерационные методы наиболее удобны для реализации на компьютере. Пусть необходимо найти корень уравнения f (x) = 0 на ин-

тервале [a, b] с точностью е. Известно, что функция f(x) на этом ин- тервале непрерывна и монотонна, f(x) = x - sin(x)/cos(x), a < b, е>0. Графическая схема алгоритма решения уравнения методом поло- винного деления представлена на рис. 23, программа в pr25.

 

Hачало

 

 

 

Bвод

 

 

 

a, b, e

 

 

 

Да

 

 

 

f (af (b)>0

 

 

 

Нет

 

 

Bывод

x1 := a

 

 

"Корней

x2 := b

 

 

нет"

 

 

 

 

 

 

x1− x2 > e

Да

 

 

Нет

x =

x1 + x2

 

 

 

 

 

2

 

 

 

Нет

 

Bывод

f(xf(x1)>0

 

 

Да

 

x1

 

 

Kонец

 

x1:=x

 

 

 

 

Рис. 23. Алгоритм PR25

// Пример pr25

 

 

 

226

#include <iostream> #include <cmath> using namespace std; float f ( float xf )

{

return xf-sin (xf)-0.25;

}

int main ()

{

float a, b, e, x, x1, x2;

printf ( "\n Введите a, b, e" ); scanf ( "%f%f%f", &a, &b, &e );

if ( f (a)*f (b)>0 ) printf("\nКорней нет\n"); else

{

x1 = a;

x2 = b;

while ( fabs (x2-x1) > e )

{

x = (x1+x2)/2;

if ( f(x1)*f(x) > 0 ) x1=x;

else

x2 = x;

}

printf("\nКореньуравнения=%f \nf (x)=%f",x1,f(x1));

}

return 0;

}

Сначала проверяется, есть ли корень на заданном интервале, т. е. ме- няются ли знаки на концах интервала f (a) × f (b) > 0 . Так как функция на ин- тервале монотонна и непрерывна, то смена знака указывает на существова- ние корня. Если корень существует, то обозначаем концы интервала новыми идентификаторами х1 и х2. Проверяем, достаточно ли мал интервал, на ко- тором находится корень fabs(x2-x1)>e. Если интервал больше точности, то находим среднее значение х на интервале. В зависимости от того, на какой

половине [x1,x] или [x,x2], происходит смена знака, интервал соответ- ствующим образом сужается. Как только длина интервала становится равной или меньшей е, процесс завершается и любой из концов интервала можно считать корнем уравнения с точностью е.

227

В языке С++ функции можно размещать в отдельном файле. В этом случае файл, из которого функция вызывается, должен содержать директиву

препроцессора #include "имя_файла.cpp".

5.1. Функции и массивы

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

Впримере pr26 дано два массива a[n] и b[m]. Требуется определить,

вкаком массиве сумма элементов наибольшая. На рис. 24 представлена схе- ма алгоритма главной функции (main), а на рис. 25 – схема функции sum, вычисляющей сумму элементов одномерного массива.

Рис. 24. лодлододлдо

228

//Пример 26 #include <iostream> #include <cstdlib> using namespace std;

float sum (int *x, int nx)

{

int i;

float s = 0;

Рис. 25. рншнгнщ8н

for ( i=0; i<nx; i++ ) s=s+x[i];

return s;

}

// Основная программа int main()

{

int n, m; //Количество элементов в массивах

229

int *a, *b; //Описываем переменные типа указатель int i,

sa, sb;//Суммы элементов массивов

printf ( "\nВведите размерность массива а n=" ); scanf ( "%d", &n);

printf ( "\n Введите размерность массива b m=" ); scanf ( "%d", &m);

//Выделение памяти под массивы a = new int[n];

b = new int[m]; randomize();

for ( i=0; i<n; i++ ) // Формируем массив a a[i] = random(10);

for ( i=0; i<m; i++ ) // Формируем массив b b[i] = random(10);

//Контрольный вывод элементов массивов printf ( "Массив а:");

for ( i=0; i<n; i++ ) printf ( "%5d", a[i]); printf ( "\nМассив b:" ); for ( i=0; i<m; i++ ) printf ( "%5d", b[i]); sa = sum (a, n);

sb = sum (b, m); if ( sa>sb )

printf ( "\n Сумма элементов массива а больше суммы элементов массива b");

else

if ( sa == sb )

printf ( "\n Сумма элементов массива а равна

сумме элементов массива b" );

else

printf ( "\n Сумма элементов массива а

меньше суммы элементов массива b" );

return 0;

}

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

Вследующем примере осуществляется циклический сдвиг одномерно- го массива из n элементов на k позиций. Например, если n = 7, а k = 3, то мас-

230