Слайд 1ФУНКЦИИ
ФУНКЦИИ ИСПОЛЬЗУЮТСЯ ДЛЯ НАВЕДЕНИЯ ПОРЯДКА В ХАОСЕ АЛГОРИТМОВ.
Б. СТРАУСТРУП
Слайд 2ФУНКЦИИ
Объявление и определение функций.
Параметры функции.
Функции с переменным числом параметров.
Рекурсивные
функции.
Слайд 3Функция — это именованная последовательность описаний и операторов, выполняющая какое-либо
законченное действие.
Слайд 4Любая программа на C состоит из функций, одна из которых
должна иметь имя main (с нее начинается выполнение программы)
Объявление и
определение функций.
Слайд 5ОБЪЯВЛЕНИЕ И ОПРЕДЕЛЕНИЕ ФУНКЦИЙ.
Функция начинает выполняться в момент вызова
Любая
функция должна быть объявлена и определена
Как и для других величин,
объявлений может быть несколько, а определение только одно.
Объявление функции должно находиться в тексте раньше ее вызова
для того, чтобы компилятор мог осуществить проверку правильности вызова.
Слайд 6ОБЪЯВЛЕНИЕ И ОПРЕДЕЛЕНИЕ ФУНКЦИЙ
Объявление функции
прототип, заголовок, сигнатура
задает ее имя,
тип возвращаемого значения и список передаваемых параметров
Определение функции
содержит, кроме объявления,
тело функции, представляющее собой последовательность операторов и описаний в фигурных скобках.
[класс] тип имя ([список_параметров]) [throw(исключения)]
{тело функции}
Слайд 7Составные части определения
[класс] тип имя ([список_параметров]) [throw(исключения)] {тело функции}
[класс]
необязательный модификатор
с помощью которого можно явно задать область видимости функции, используя
ключевые слова extern и static
extern — глобальная видимость во всех модулях программы (по умолчанию)
static — видимость только в пределах модуля, в котором определена функция
Слайд 8Составные части определения
[класс] тип имя ([список_параметров]) [throw(исключения)] {тело функции}
тип
Тип возвращаемого
функцией значения
может быть любым, кроме массива и функции, но может
быть указателем на массив или функцию
если функция не должна возвращать значение, указывается тип void
Слайд 9Составные части определения
[класс] тип имя ([список_параметров]) [throw(исключения)] {тело функции}
[список_параметров]
определяет величины,
которые требуется передать в функцию при ее вызове
Элементы списка параметров
разделяются запятыми.
Для каждого параметра, передаваемого в функцию, указывается его тип и имя.
В объявлении имена можно опускать
Слайд 10Составные части определения
[класс] тип имя ([список_параметров]) [throw(исключения)] {тело функции}
[throw(исключения)]
Об исключениях
(аварийных ситуациях), обрабатываемых функцией,
мы поговорим позже
Слайд 11Функцию можно определить как встроенную с помощью модификатора inline.
компилятор вместо
обращения к функции помещает ее код непосредственно в каждую точку
вызова
Слайд 12ОБЪЯВЛЕНИЕ И ОПРЕДЕЛЕНИЕ ФУНКЦИЙ.
Директива inline носит рекомендательный характер и выполняется
компилятором по мере возможности.
Использование inline-функций может увеличить объем исполняемой программы.
Определение inline-функции должно предшествовать ее вызовам
иначе вместо inline-расширения компилятор сгенерирует обычный вызов.
Слайд 13Пример функции, возвращающей сумму двух целых величин
#include
int sum(int a,
int b); // объявление функции
int main(){
int a = 2, b =
3, с, d;
с = sum(a, b); // вызов функции
cin >> d;
cout << sum(c, d); // вызов функции
return 0;
}
int sum(int a, int b){ // определение функции
return (a + b);
}
Слайд 14Пример функции, выводящей на экран поля переданной ей структуры
#include
struct Worker{
char fio[30];
int date, code;
double salary;
};
void print_worker(Worker); //объявление
функции
int main(){
Worker staff[100];
. . .
/* формирование массива staff */.
for (int i = 0; i<100; i++)print_worker(staff[i]);
// вызов функции return 0;
}
void print_worker(Worker w){ //определение функции
cout << w.fio << ' ' << w.date << ' ' << w.code << ' ' << w.salary << endl;
}
Слайд 15Значения локальных переменных между вызовами одной и той же функции
не сохраняются.
Если этого требуется избежать, при объявлении локальных переменных используется
модификатор static.
Слайд 16Модификатор static
#include
void f(int a){
int m = 0;
cout
0;
int p = 0;
cout << n++ << ' ' << m++ << ' ' << p++ << '\n';
}
}
int main(){ f(3); f(2); return 0;}
Программа выведет на экран:
Слайд 17Механизм возврата из функции в вызвавшую ее функцию реализуется оператором
return [ выражение ];
Слайд 18ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ.
Функция может содержать несколько операторов return.
Если функция описана как
void, выражение после return не указывается.
оператор return можно опускать
для функции типа void, если возврат из нее происходит перед закрывающей фигурной скобкой, и для функции.
Выражение, указанное после return, неявно преобразуется к типу возвращаемого функцией значения и передается в точку вызова функции
Слайд 19Возвращаемое значение
Примеры:
int fl(){return 1;} // правильно
void f2(){return 1;} // неправильно,
/*
f2 не должна возвращать значение */
double f3(){return 1;} // правильно,
/* 1 преобразуется к типу double */
Нельзя возвращать из функции указатель на локальную переменную
int* f(){ int a = 5;
return &a; // нельзя!
}
память, выделенная локальным переменным при входе в функцию, освобождается после возврата из нее
Слайд 20Механизм параметров является основным способом обмена информацией между вызываемой и
вызывающей функциями
Параметры функции
Слайд 21Параметры функции
При вызове функции:
1
вычисляются выражения, стоящие на месте аргументов
2
в стеке
выделяется память под формальные параметры функции в соответствии с их
типом
3
каждому из параметров присваивается значение соответствующего аргумента
4
проверяется соответствие типов и при необходимости выполняются их преобразования
5
при несоответствии типов выдается диагностическое сообщение
Слайд 22Существует два способа передачи параметров в функцию:
по значению и
по адресу
Слайд 23ПАРАМЕТРЫ ФУНКЦИИ.
При передаче по значению в стек заносятся копии значений
аргументов.
Операторы функции работают с этими копиями.
Доступа к исходным значениям
параметров у функции нет, а, следовательно, нет и возможности их изменить.
При передаче по адресу в стек заносятся копии адресов аргументов.
Функция осуществляет доступ к ячейкам памяти по этим адресам и может изменить исходные значения аргументов.
Слайд 24Параметры функции
Пример:
#include
void f(int i, int* j, int& k);
int
main(){
int i = 1, j = 2, k
= 3;
cout << "i j k\n";
cout << i <<' '<< j <<' '<< k <<'\n';
f(i, &j, k);
cout << i <<' '<< j <<' '<< k;
return 0;
}
void f(int i, int* j, int& k){
i++; (*j)++; k++; }
Программа выведет на экран:
Первый параметр (i) передается по значению. Его изменение в функции не влияет на исходное значение.
Второй параметр (j) передается по адресу с помощью указателя, при этом для передачи в функцию адреса фактического параметра используется операция взятия адреса, а для получения его значения в функции требуется операция разыменования.
Третий параметр (k) передается по адресу с помощью ссылки.
Слайд 25По умолчанию параметры любого типа,
кроме массива и функции,
передаются
в функцию
по значению
Слайд 26ПЕРЕДАЧА МАССИВОВ В КАЧЕСТВЕ ПАРАМЕТРОВ.
При использовании в качестве параметра массива
в функцию передается указатель на его первый элемент.
массив всегда передается
по адресу.
При этом информация о количестве элементов массива теряется.
размерность массива следует передавать через отдельный параметр.
Слайд 27Передача массивов в качестве параметров.
#include
int sum(const int* mas, const
int n);
int const n = 10;
int main(){
int mas[n] = {3,
4, 5, 4, 4};
cout << "Сумма элементов массива: " << sum(mas, n);
return 0; }
int sum(const int* mas, const int n){
/* варианты: int sum(int mas[], int n)
или int sum(int mas[n], int n) */
// (величина n должна быть константой)
int s = 0;
for (int i = 0; ireturn s; }
Слайд 28ПЕРЕДАЧА МНОГОМЕРНЫХ МАССИВОВ В КАЧЕСТВЕ ПАРАМЕТРОВ.
При передаче многомерных массивов все
размерности, если они не известны на этапе компиляции, должны передаваться
в качестве параметров.
Внутри функции массив интерпретируется как одномерный, а его индекс пересчитывается в программе.
Слайд 29Функцию можно вызвать через указатель на нее.
Для этого объявляется указатель
соответствующего типа и ему
с помощью операции взятия адреса присваивается
адрес функции.
Слайд 30Передача имен функций в качестве параметров
void f(int a ){/*...*/ } //
определение функции
void (*pf)(int); // указатель на функцию
. . . . .
. .
pf = &f; // указателю присваивается адрес функции
// (можно написать pf = f;)
pf(10); // функция f вызывается через указатель pf
// (можно написать (*pf)(10))
Слайд 31ПЕРЕДАЧА ИМЕН ФУНКЦИЙ В КАЧЕСТВЕ ПАРАМЕТРОВ.
Указатели на функции передаются в
подпрограмму таким же образом, как и параметры других типов.
Тип
указателя и тип функции, которая вызывается посредством этого указателя, должны совпадать в точности.
Слайд 32Передача имен функций в качестве параметров
#include
typedef void (*PF)(int);
void
f1(PF pf){
// функция f1 получает в качестве параметра указатель
типа PF
pf(5); // вызов функции, переданной через указатель
}
void f(int i){cout << i;}
int main(){
f1(f);
return 0;
}
Слайд 33Параметры со значениями по умолчанию
Чтобы упростить вызов функции, в ее
заголовке можно указать значения параметров по умолчанию
Эти параметры должны быть
последними в списке и могут опускаться при вызове функции
Если при вызове параметр опущен, должны быть опущены и все параметры, стоящие за ним
В качестве значений параметров по умолчанию могут использоваться константы, глобальные переменные и выражения
Слайд 34Параметры со значениями по умолчанию
int f(int a, int b =
0);
void f1(int, int = 100, char* = 0);
// обратите
внимание на пробел между * и =
// без него получилась бы операция сложного присваивания *=
void err(int errValue = errno); // errno - глобальная переменная
. . . . . . .
f(100); f(a, 1); // варианты вызова функции f
f1(a); f1(a, 10); f1(a, 10, "Vasia"); // варианты вызова функции f1
f1(a,,"Vasia") // неверно!
Пример:
Слайд 35Если список формальных параметров функции заканчивается многоточием, это означает, что
при ее вызове на этом месте можно указать еще несколько
параметров.
Функции с переменным числом параметров
Слайд 36ФУНКЦИИ С ПЕРЕМЕННЫМ ЧИСЛОМ ПАРАМЕТРОВ.
Проверка соответствия типов для параметров обозначенных
многоточием не выполняется!
char и short передаются как int
float —
как double
Слайд 37Функции с переменным числом параметров
В качестве примера можно привести функцию
printf, прототип которой имеет вид:
int printf(const char*, ...);
Это означает, что
вызов функции должен содержать по крайней мере один параметр типа char* и может либо содержать, либо не содержать другие параметры
printf("Введите исходные данные"); // один параметр
printf("Сумма: %5.2f рублей", sum); // два параметра
printf ("%d %d %d %d", а, Ь, с, d); // пять параметров
Слайд 38ФУНКЦИИ С ПЕРЕМЕННЫМ ЧИСЛОМ ПАРАМЕТРОВ.
Все аргументы, заданные в вызове функции
с переменным числом параметров, размещаются в стеке.
Количество формальных параметров,
объявленных для функции, определяется числом аргументов, которые берутся из стека и присваиваются формальным параметрам.
Слайд 39За правильность выбора дополнительных аргументов из стека и определение числа
аргументов, находящихся в стеке
отвечает программист!
Слайд 40ФУНКЦИИ С ПЕРЕМЕННЫМ ЧИСЛОМ ПАРАМЕТРОВ.
Для обеспечения удобного способа доступа к
аргументам функции с переменным числом параметров имеются три макроопределения (макросы)
va_start, va_arg, va_end, находящиеся в заголовочном файле stdarg.h.
Эти макросы указывают на то, что функция, разработанная пользователем, имеет некоторое число обязательных аргументов, за которыми следует переменное число необязательных аргументов.
Обязательные аргументы доступны через свои имена как при вызове обычной функции.
Слайд 41Функции с переменным числом параметров
va_start
предназначен для установки аргумента arg_ptr на
начало списка необязательных параметров
имеет вид функции с двумя параметрами
void va_start(arg_ptr,prav_param);
Слайд 42МАКРОКОМАНДА VA_START.
Параметр prav_param должен быть последним обязательным параметром вызываемой функции,
а указатель arg_prt должен быть объявлен с предопределением в списке
переменных типа va_list в виде:
va_list arg_ptr;
Макрос va_start должен быть использован до первого использования макроса va_arg.
Слайд 43Функции с переменным числом параметров
va_arg
обеспечивает доступ к текущему параметру вызываемой
функции
имеет вид функции с двумя параметрами
type_arg va_arg(arg_ptr,type);
Слайд 44МАКРОКОМАНДА VA_ARG.
Макрос va_arg используется столько раз, сколько необходимо для извлечения
всех параметров вызываемой функции.
Слайд 45Функции с переменным числом параметров
va_end
используется по окончании обработки всех параметров
функции и устанавливает указатель списка необязательных параметров на ноль (NULL).
Слайд 46ПРИМЕР ФУНКЦИИ С ПЕРЕМЕННЫМ ЧИСЛОМ ПАРАМЕТРОВ.
Рассмотрим применение макросов для обработки
параметров функции вычисляющей среднее значение произвольной последовательности целых положительных чисел.
Поскольку функция имеет переменное число параметров будем считать концом списка значение равное -1.
Поскольку в списке должен быть хотя бы один элемент, у функции будет один обязательный параметр.
Слайд 47Пример функции с переменным числом параметров
#include
#include
int main() {
int n;
int sred_znach(int,...);
n=sred_znach(2,3,4,-1); // вызов с
четырьмя параметрами
printf("n=%d",n);
n=sred_znach(5,6,7,8,9,-1); // вызов с шестью параметрами
printf("n=%d",n);
return (0);
}
Слайд 48Пример функции с переменным числом параметров
int sred_znach(int x,...){
int i=0, j=0, sum=0;
va_list uk_arg;
// установка указателя
uk_arg на первый необязятельный параметр
va_start(uk_arg,x);
if (x!=-1) sum=x; /* проверка на пустоту списка */
else return (0);
j++;
// выборка очередного параметра и проверка на конец списка
while ( (i=va_arg(uk_arg,int))!=-1)
{
sum+=i;
j++;
}
va_end(uk_arg); /* закрытие списка параметров */
return (sum/j);
}
Слайд 49ЗАМЕЧАНИЯ К ПРИМЕРУ.
Тип va_list предназначен для хранения указателя на очередной
аргумент.
typedef void_FAR *va_list;
Макрос va_start инициализирует этот указатель.
Макрос va_arg возвращает
значение очередного аргумента, каждый его вызов приводит к продвижению указателя, хранящегося в va_list.
После перебора аргументов, но до выхода из функции с переменным числом аргументов необходимо обратиться к макросу va_end.
Слайд 50Поскольку компилятор не имеет информации для контроля типов, вместо функций
с переменным числом параметров предпочтительнее пользоваться параметрами по умолчанию или
перегруженными функциями!
хотя можно представить случаи, когда переменное число параметров является лучшим решением
Слайд 51Рекурсивной называется функция, которая вызывает саму себя.
Такая рекурсия называется прямой.
Существует
еще косвенная рекурсия, когда две или более функций вызывают друг
друга.
Рекурсивные функции
Слайд 52РЕКУРСИВНЫЕ ФУНКЦИИ.
В момент вызова функцией самой себя:
в стеке создается копия
значений ее параметров, как и при вызове обычной функции,
после
чего управление передается первому исполняемому оператору функции.
При повторном вызове этот процесс повторяется.
Для завершения вычислений каждая рекурсивная функция должна содержать хотя бы одну нерекурсивную ветвь алгоритма, заканчивающуюся оператором возврата.
При завершении функции соответствующая часть стека освобождается, и управление передается вызывающей функции, выполнение которой продолжается с точки, следующей за рекурсивным вызовом
Слайд 53Рекурсивные функции.
Пример:
// вычисление факториала
long fact(long n){
if (n==0 || n==l)
return 1;
return (n * fact(n - 1));
}
// To
же самое можно записать короче:
long fact(long n){
return (n>l) ? n * fact(n - 1): 1;
}
Слайд 54РЕКУРСИВНЫЕ ФУНКЦИИ.
Рекурсивные функции чаще всего применяют для компактной реализации рекурсивных
алгоритмов, а также для работы со структурами данных, описанными рекурсивно,
например, с двоичными деревьями.
Любую рекурсивную функцию можно реализовать без применения рекурсии, для этого программист должен обеспечить хранение всех необходимых данных самостоятельно.
Достоинством рекурсии является компактная запись, а недостатками — расход времени и памяти на повторные вызовы функции и передачу ей копий параметров, и, главное, опасность переполнения стека.