Функции. Основные правила использования




ФУНКЦИЙ

 

Цель работы

Изучение способов объявления, описания и вызова функций, а также способов передачи и преобразования данных функциями.

Назначение функций

 

Функция – это самостоятельная единица программы, спроектированная для выполнения описанной группы действий. Операндами этой группы дей­ствий являются локальные и формальные параметры (далее параметры) функ­ции. Выполнение этой группы действий сводится к вызову функции и переда­че ей фактических аргументов (далее аргументы).

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

#include <stdio.h>

/* Объявления функций * /

float get_param(void); /* Функция ввода одного значения */

float get_square(float p); /* Функция вычисления квадрата аргумента */

void put_square(float s); /* Функция вывода на экран значения */

/* Описание глобальной константы */ const float MAXF=100;

/* 0писание главной функции */

void main(void)

{ float a, s; a=get_param0; s=get_square(a); put_square(s); ^

/* 0писания функций */

float get_param(void)

{ float p; printf ("Введите число:"); scanf ("%f',&p); return (p); }

float get_square(float r)

{ if(r>MAXF || r<-MAXF) return (-MAXF); else return (r*r); }

void put_square(float s)

{ if (s = -MAXF) printf ("Внимание, переполнение^");

else printf ("Квадрат введенного числа равен: %f\n",s);

}

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

1. main вызывает функцию get_param, которая выводит сообщение

«Введите число» и ждет ввода числа, затем присваивает введенное значение локальной переменной р и возвращает его значение, main возвращенное значение помещает в точку ее вызова, в правую часть оператора присвоения. То есть переменной а присваивается возвращенное функцией get_param значение.

2. Далее main вызывает функцию get_square и передает аргумент – зна­чение переменной a. get_square присваивает значение аргумента параметру г и возвращает значение выражения г*г или -MAXF.

3. main возвращенное значение присваивает переменной s и вызывает функцию put_square и передает ей аргумент - значение s. put_square присваи­вает значение аргумента параметру s и в дальнейшем работает с локальной переменной s (переменные s в функциях main и put_square - это различные переменные). Функция put_square в зависимости от значения параметра s выводит то или иное сообщение на экран и возвращает управление (не значение) в точку ее вызова.

4. main возвращает управление в ту точку, откуда она была вызвана, например операционной системе.

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

Вызовы функций могут быть практически в любом месте. Например, функции в качестве аргумента могут использовать вызов функции. Функцию main в предыдущем примере можно было описать в следующем виде:

void main(void) { put_square(get_square(get_param())); }

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

Если к функции относиться как к «черному ящику», то для ее использо­вания достаточно знать, как к ней обращаться и как установить связи между функцией и вызывающей программой. Для этого существуют понятия «описа­ние», «объявление» и «вызов» функции.

Описание функции

Любая функция, вызванная в программе, должна быть где-то описана (только один раз).

Синтаксис описания функции следующий:

<тип> <имя_функции>(<список_параметров>) <тело_функции>

<тип> определяет тип значения выражения оператора return. Если опе­ратор return не задан или не содержит выражения, то в качестве типа возвращаемого значения указывают void. Например, функции get_param и get_square возвращают значения типа float, а функция put_square не возвращает значе­ния. Функции не могут возвращать массивов или функций, но могут возвра­щать указатели на любой тип, включая массивы и функции.

<имя_функции> - любой идентификатор, который должен отличаться от имен типов, переменных и перечислимых констант с той же областью ви­димости. Например, функция func возвращает значение типа double и не име­ет аргументов:

double func(void).

Если функция должна возвращать указатель на <тип>, то слева от имени функции ставят звездочку. Например, функция func возвращает указа­тель на значение типа long:

long *func(void).

<список_параметров> указывает типы, имена параметров и порядок присвоения им значений аргументов при вызове функции. Если функция не использует параметров, то список содержит только ключевое слово void. Иначе в круглых скобках через запятую перечисляют описания параметров

<имя_типа> <имя_параметра>.

Примеры:

int f(void) - функция f не имеет параметров.

int p(int k, long m) – функция р имеет два параметра: первый типа int с именем k и второй типа long с именем m.

Компилятор при необходимости выполняет преобразования парамет­ров. После преобразования не остается параметров короче, чем int и типа float. Т. е., описать параметр типом char все равно, что описать его типом int.

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

int printf(char * format,...).

Имена параметров нельзя использовать для других переменных в теле функции.

<тело_функции> – это составной оператор, в котором описаны группа действий и, возможно, локальные константы, типы и переменные. В теле фун­кции нельзя описывать другую функцию. Описания локальных объектов име­ют силу только внутри этой функции.

Объявление функции

Синтаксис объявления от описания функции главным образом отлича­ется заменой тела функции символом; и имеет следующий вид:

<тип> <имя_функции>(<список_параметров>);

Объявление позволяет вызывать функцию до описания или из другого файла. Объявление позволяет компилятору контролировать корректность

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

Объявления функций также упрощают чтение текста и отладку прог­раммы. Например, в объявлении функции параметрам можно давать одни (удобно читаемые) имена и они будут использованы в сообщениях компиля­тора, а в описании функции – другие.

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

int func (int, double, int);

Вызов функции

Вызов функции - это выражение, которое передает управление и аргу­менты (если они есть) функции, и имеет следующий синтаксис:

<выражение> ([<список_выражений>])

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

<список_выражений>, в котором выражения следуют через запятую, представляет список (возможно пустой) аргументов, посылаемых функции.

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

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

Значения выражений списка могут вычисляться в любом порядке, так что выражения с побочными эффектами могут дать непредсказуемые резуль­таты. Например, следует избегать вызовов вида f(i-2,i=k+l).

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

Рассмотрим пример программы:

#include <stdio.h> /* Вызываемая функция */ void f(int val, int& ref) { val++; ref++; } /* Вызывающая функция */

void main(void) { int r = l, j = l;

f(i, j); /* При вызове функции f передаем значение переменной i и указатель на переменную j */

printf("\ni=%dj = %d”,i,j);

}

В результате ее работы на экран будет выведена строка:

i=l j=2

Это объясняется следующим образом. Вызываемая функция f через па­раметр int val получила значение переменной i (т.е. 1), а через параметр int& ref получила адрес переменной j вызывающей функции main. Оператор val++; увеличивает значение локальной переменной val на единицу. Оператор ref++; увеличивает значение переменной j на единицу, поскольку ref указы­вает на переменную j функции main.

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

int g(const int&, long);

При вызове управление передается первому оператору функции. Вы­полнение оператора return в теле функции возвращает управление и, возмож­но, значение возврата в вызывающую функцию. Если оператор return не вы­полнен, то управление возвращается после выполнения последнего оператора тела функции. При этом величина возврата не определена.

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

int factorial (int n) {return (п>1)? n*factorial(n-l): 1; }

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

Пример выполнения задания.

Поменть местами значения переменных а, Ь. Текст программы может иметь следующий вид:

#include <stdio.h>

/* Объявление функции*/

void interchange(double& refl, double& ref2);

/*0писание главной функции*/

void main(void)

{ double a=4.521,b = 21.46;

printf ("\пДо вызова функции: a=%lf, b=%lf", a, b);

interchange(a, b);

printf ("\пПосле вызова функции: a=%lf, b=%lf", a, b);

} /*0писание функции*/

void interchange(double& геf1, double& ref2);

{ double temp;

temp = ref1; ref1 = ref2; ref2 = temp;

}

В результате работы этой программы на экране будет напечатано:

До вызова функции: а=4.521, b=21.46. После вызова функции: а=21.46, Ь=4.521.

Контрольные вопросы

1. Чем отличается тело функции от составного оператора?

2. Каковы ограничения на функции с одинаковым именем, если они имеют параметры со значениями по умолчанию?

3. Как объявить функцию, если аргументом по умолчанию является указатель на переменную?

4. С именами каких объектов программы не должно совпадать имя функции?

5. Каким образом используется тип возвращаемого значения?

 

Варианты заданий

Во всех вариантах подразумевается описание не менее одной функции. В вариантах 1–4 текст программы должен содержать только один знак операции отношения.

1. Поменять значения переменных а, Ь, с местами так, чтобы они удов­летворяли неравенствам а > b > с.

2. Вывести на экран значение той из переменных а, b, с, которое мини­мально по абсолютной величине.

3. Вывести на экран минимальное из значений а и b, где а и b – разные сочетания выражений со значениями типа int, long, float и double.

4. Поменять значения переменных а, b, с, d местами так, чтобы они удовлетворяли неравенствам a<b<c<d.

5. Поменять значения переменных а, b, с так, чтобы на месте перемен­ной а оказалось значение а+b+с, на месте b - а-b-с, на месте с - а*b*с.

6. Вычислить корни уравнения a*x2+b*x+c=0 при различных вещест­венных аргументах а, b, с.

В вариантах 7-13 в функциях значение n в вызове указывает макси­мальное количество итераций, a eps - необходимую точность. На месте п должно оказаться количество использованных итераций.

7. Оценить корни уравнения x2+a*cos(x)=0 при различных значениях вещественного аргумента а.

8. Вычислить сумму ряда l+x+x2/2!+xз/3!+...+xn/n!+... при различных значениях вещественного аргумента х.

9. Вычислитьсумму ряда x-xз/3!+x5/5!+...+(-l)nx(2n+l)/(2n+l)!+... при различных значениях вещественного аргумента х.

10. Методом половинного деления оценить корни уравнения x2 + f *sin(x) = 0 при различных значениях вещественного аргумента f.

11. Методом итераций оценить корни уравнения x2 + f *sin(x) º 0 при различных значениях вещественного аргумента f.

12. Вычислить сумму1+х+... +((n2+l)n!)(x/2)n+... сзаданной погрешностью eps или заданного п членов ряда.

13. В диапазоне [а, Ь] определить диапазон [с, d], в котором находится один корень уравнения f(x)=x2+q*sin(x) =0, и |f(x)|<eps.

14. Вычислить целую степень вещественного числа:

15. Вычислить коэффициенты c(i,n) при всех степенях полинома (1+х)". Использовать формулы

c(0,n)=c(n,n)=l; c(i,n)=c(i,n-l)+c(i-l,n-l); 0<i<n.

16. Вычислить количество слов и длину самого длинного слова в стро­ке.

17. Кодировать вводимый с клавиатуры текст - все символы увеличить на величину a*n 2 + b*n + c (n – позиция символа в строке, а, Ь, с – аргументы функции). Затем восстановить исходный текст и вернуть количество ошибoчно восстановленных символов.

18. Определить наличие символов с 1, с2, с З, с 4 в строке. Отсутствующие символы заменить наиболее близкими из строки.

19. Преобразовать строку цифр в число и вернуть его.

20. Определить наличие баланса открывающих и закрывающих скобок в строке. Скобки могут быть заданного аргументами типов.

21. Вычислить значение полинома

Pn (x) = anxn + an –1 xn –1 + … + a 2 x 2 + a 1 x + a 0 пятого порядка (п = 5)по схеме Горнера

Pn (x) = (…((an) x + an –1) x + an –2) x + … a 1) x + a 0значение функции

z (x)=(12 x 5-3 x 2+5 x)/(4 x з+5 x 2+3)

при различных значениях х.

22. Вычислить факториал и коэффициенты c(i,n)=n!/(i!(n-i)!), 0<i<n<nmax. Определить максимально возможное значение n max.

23. Определить наличие символов с1, с2, сЗ, с4 в строке в порядке их следования в списке аргументов.

 

 

Лабораторная работа № 13

 



Поделиться:




Поиск по сайту

©2015-2024 poisk-ru.ru
Все права принадлежать их авторам. Данный сайт не претендует на авторства, а предоставляет бесплатное использование.
Дата создания страницы: 2017-04-20 Нарушение авторских прав и Нарушение персональных данных


Поиск по сайту: