Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
книги / 277.pdf
Скачиваний:
11
Добавлен:
07.06.2023
Размер:
1.06 Mб
Скачать

Пользовательские функции

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

Наиболее эффективными, с точки зрения производительности труда программистов, стали так называемые языки программирования высокого уровня, такие как C, Basic, Pascal и др. Разрабатывать программы на языках высокого уровня значительно проще, а ошибок при создании программ допускается гораздо меньше. Заложенные в языках высокого уровня идеи структурного (процедурного) программирования позволил программистам в разумные сроки (несколько месяцев) создавать приложения объемом в сотни тысяч строк.

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

вводит массив чисел;

определяет среднее арифметическое чисел;

определяет местоположение максимального элемента;

заменяет максимальный элемент массива средним арифметическим;

выводит обработанный массив.

Операторную часть такой программы можно записать следующим образом:

97

{........

vvodmas(mas);

sr=sredmas(mas);

max=maxmas(mas);

zamena(mas);

vivod(mas);

........

}

Как видно из приведенного фрагмента, программа разделена по смыслу на пять подпрограмм, которые еще следует написать. В программе на языке С++ описание функций может располагаться либо до функции main, либо после функции main. Никакая функция не может быть описана внутри другой функции. Кроме того, функции могут быть описаны в отдельном файле и подключены к программе с помощью директивы препроцессора include. Каждая функция описывается один раз, а вызываться на выполнение может многократно. В языке C++ описание должно обязательно предшествовать вызову функции. Если функция описана после функции, из которой она вызывается, то до вызова функции должен быть помещен прототип функции. Прототип функции - это заголовок функции, завершающийся точкой с запятой (функция должна быть декларирована).

Структура подпрограммы почти полностью повторяет структуру главной функции, что как раз подчеркивает структурированность языка. В функциях могут быть описаны собственные константы, типы, переменные. В этом случае они называются локальными. Их область действия распространяется только на те функции, в которых они описаны. Переменные, описанные вне функций, в том числе и вне главной функции, называются глобальными. Их область действия распространяется на все функции, расположенные в том же файле после описания. Имена локализованных переменных могут совпадать с ранее объявленными глобальными именами. В этом случае считается, что локальное имя «закрывает» глобальное и делает его недоступным, т. е. одноименные локальные и глобальные переменные — это разные переменные. Память под глобальные переменные отводится до начала выполнения программы и освобождается в момент завершения программы. Доступ к ним возможен из любой функции. Память под локальные переменные выделяется в момент вызова подпрограммы и освобождается после завершения её выполнения. Доступ к ним возможен только из той подпрограммы, в которой они описаны.

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

заголовок функции тело функции

98

Общая форма записи заголовка функции:

<тип> <имя> ([<список формальных параметров>])

Здесь <имя> - правильный идентификатор, который используется для обращения к функции;

<тип> - тип возвращаемого функцией результата; <список формальных параметров> - включает в себя параметры,

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

void sum(float a, float b, float *s) void sk (int *a, int c , float d) float f (float x, float &y)

int ab (int a) void par (void)

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

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

<имя >(<список фактических параметров>); Он записывается в том месте программы, где по логике решения

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

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

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

99

{ 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, и это число присваивается переменной с. При втором и третьем обращениях

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

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

//Пример pr23 #include <stdio.h> #include <math.h> #include <conio.h>

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

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

void main()

{

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

printf(« \n Введите координаты точек:"); scanf("%f%f%f%f%f%f»,&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);

printf("Периметр треугольника равен %4.2f», a+b+c); getch();

}

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

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

100

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

¾тип каждого фактического должен совпадать с типом

соответствующего формального параметра.

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

//Пример pr24 #include <stdio.h> #include <stdlib.h> #include <conio.h>

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

{

return (af+bf)/cf;

}

void 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 );

}

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

101

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

Для этой же задачи составим программу с использованием параметров, передаваемых по ссылке. В языке С этого способа передачи параметров не было. Понятие ссылка появилось только в С++. Ссылочные переменные описываются следующим образом:

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

<инициализирующее выражение> - выражение, определяющее местоположение в памяти. Например:

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

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

//Пример pr26

#include <stdio.h> #include <conio.h>

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

{

yf = (af+bf)/cf;

}

void main ()

{

float a, b, c, x, y;

102

Соседние файлы в папке книги