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

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

Цель работы

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

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

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

Программа может содержать сколь угодно много различных функций, и все они равноправны. Главная функция 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.

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